openapi: 3.1.0
info:
  title: Kavanah API
  version: 1.0.0
  description: |
    RESTful API for the Kavanah project management platform.
    All endpoints (except auth) require a valid session cookie.
  contact:
    name: Kavanah Support
servers:
  - url: http://localhost:3000
    description: Local development
paths:
  /api/apps:
    get:
      summary: List the workspace's installed community apps
      tags:
        - apps
      x-required-permission: owner/admin
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/apps/installs/{id}:
    delete:
      summary: Uninstall a community app from the workspace
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
    patch:
      summary: Reconfigure an installed app (enable, agent access, scopes)
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                agentAccess:
                  type: boolean
                grantedScopes:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/apps/installs/{id}/keys:
    get:
      summary: List an installed app's API keys (metadata only)
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
    post:
      summary: Mint a per-app API key for an installed app (returned once)
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  default: App key
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/apps/installs/{id}/keys/{keyId}:
    delete:
      summary: Revoke a single per-app API key
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: keyId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/apps/installs/{id}/secret:
    post:
      summary: Mint or rotate an install's webhook signing secret (returned once)
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/apps/{slug}/install:
    post:
      summary: Install a community app for the workspace with granted scopes
      tags:
        - apps
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                grantedScopes:
                  type: array
                  items:
                    type: string
                agentAccess:
                  type: boolean
      responses:
        "201":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps:
    get:
      summary: List community apps this workspace owns (developer console)
      tags:
        - developer
      x-required-permission: owner/admin
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
    post:
      summary: Create a new private community app from a manifest
      tags:
        - developer
      x-required-permission: owner/admin
      responses:
        "201":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}:
    delete:
      summary: Delete a private app the workspace owns
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
    get:
      summary: Get authoring detail for one owned app (manifest + versions)
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}/reachability:
    post:
      summary: Check whether an owned app's tool endpoint is reachable
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}/submit:
    post:
      summary: Submit an app version for platform review (public listing)
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                versionId:
                  type: string
                  minLength: 1
              required:
                - versionId
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}/test-tool:
    post:
      summary: Send a signed test call to an owned app's tool endpoint
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}/versions:
    post:
      summary: Publish a new version of an owned app from a manifest
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "201":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}/versions/{id}:
    delete:
      summary: Delete a draft or rejected version of an owned app
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/developer/apps/{slug}/versions/{id}/rollback:
    post:
      summary: Set an older version of an owned app as the current version
      tags:
        - developer
      x-required-permission: owner/admin
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/feature-requests:
    get:
      summary: List the public feature-request board plus the caller's own submissions
      description: "Optional query params: status (active|all), category, search, limit (max 200)."
      tags:
        - Feature requests
      x-required-permission: authenticated
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
    post:
      summary: Submit a feature request
      tags:
        - Feature requests
      x-required-permission: authenticated
      responses:
        "201":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/marketplace:
    get:
      summary: List community apps visible to the workspace, with install state
      tags:
        - apps
      x-required-permission: authenticated
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/marketplace/{slug}:
    get:
      summary: Get one community app's detail and install state
      tags:
        - apps
      x-required-permission: authenticated
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
        - sessionCookie: []
        - apiKeyBearer: []
  /api/auth/signup:
    post:
      tags:
        - Auth
      summary: Create a new account
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - firstName
                - lastName
                - email
                - password
              properties:
                firstName:
                  type: string
                lastName:
                  type: string
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
      responses:
        "200":
          description: Account created and signed in
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  signedIn:
                    type: boolean
                  redirectPath:
                    type: string
                  user:
                    type: object
                    properties:
                      email:
                        type: string
        "400":
          description: Validation error or duplicate email
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/auth/login:
    post:
      tags:
        - Auth
      summary: Sign in with email and password
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                nextPath:
                  type: string
      responses:
        "200":
          description: Signed in successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  signedIn:
                    type: boolean
                  workspaceReady:
                    type: boolean
                  redirectPath:
                    type: string
                  user:
                    type: object
                    properties:
                      email:
                        type: string
        "401":
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/auth/logout:
    post:
      tags:
        - Auth
      summary: Sign out and clear session
      responses:
        "200":
          description: Signed out
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
  /api/auth/forgot-password:
    post:
      tags:
        - Auth
      summary: Request a password reset link
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
      responses:
        "200":
          description: Reset link sent (or silently ignored if email not found)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    type: string
  /api/auth/reset-password:
    post:
      tags:
        - Auth
      summary: Reset password using a token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - password
                - token
              properties:
                password:
                  type: string
                  minLength: 8
                token:
                  type: string
      responses:
        "200":
          description: Password updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  redirectPath:
                    type: string
  /api/tasks:
    get:
      tags:
        - Tasks
      summary: List tasks for a project
      x-app-scope: tasks:read
      parameters:
        - name: projectRef
          in: query
          required: true
          schema:
            type: string
        - name: includeArchived
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: |
            Include archived tasks in the result (rows carry `archived` and `archived_at`). Default false — archived tasks are hidden.
      responses:
        "200":
          description: Tasks list
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  tasks:
                    type: array
                    items:
                      $ref: "#/components/schemas/Task"
    post:
      tags:
        - Tasks
      summary: Create a new task
      x-app-scope: tasks:write
      description: |
        Input keys are accepted in camelCase or snake_case (`dueDate` / `due_date`); camelCase wins when both are present. Responses are snake_case. Retries can pass an `Idempotency-Key` header: a replay within 5 minutes returns the original task instead of creating a duplicate (replays carry `X-Idempotent-Replay: 1`). Note `kvn_complete` is a generated column — returned by GET, never writable.
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            maxLength: 255
          description: |
            Opaque client token (charset `A-Za-z0-9._:-+/=`). Repeated creates with the same key within 5 minutes return the first result instead of inserting again.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - title
              properties:
                projectRef:
                  type: string
                additionalProjectRefs:
                  type: array
                  items:
                    type: string
                  description: Secondary projects the task also appears in.
                title:
                  type: string
                description:
                  type: string
                statusId:
                  type: string
                  default: open
                tag:
                  type: string
                priority:
                  type: string
                  enum:
                    - Normal
                    - Medium
                    - High
                    - Done
                  default: Normal
                  description: Matched case-insensitively; stored in canonical casing.
                stage:
                  type: string
                  enum:
                    - fire
                    - green
                    - yellow
                    - investigate
                    - plan
                    - implement
                  nullable: true
                assignedTo:
                  nullable: true
                  oneOf:
                    - type: string
                    - type: array
                      items:
                        type: string
                dueDate:
                  type: string
                  format: date
                  nullable: true
                startDate:
                  type: string
                  format: date
                  nullable: true
                  description: Scheduled start (Timeline/Gantt bar).
                isMilestone:
                  type: boolean
                  description: Pins the task as a milestone on the roadmap.
                reminderDate:
                  type: string
                  nullable: true
                createdBy:
                  type: string
                parentTaskId:
                  type: integer
                  nullable: true
                  description: Link to a top-level parent task (first-class sub-task).
                departmentId:
                  type: string
                  nullable: true
                  description: UUID of the owning department (validated against the workspace).
                knowHow:
                  type: string
                  description: KVN K axis — method, verification, escalation.
                vision:
                  type: string
                  description: KVN V axis — the end-state.
                negation:
                  type: string
                  description: KVN N axis — what would make a working answer still wrong.
                visionRestated:
                  type: string
                  description: Delegate's V restatement (handoff loop).
                negationSurfaced:
                  type: string
                  description: Delegate's surfaced objection (handoff loop).
                subtasks:
                  type: array
                  items:
                    type: object
                notes:
                  type: array
                  items:
                    type: string
                links:
                  type: array
                  items:
                    type: object
                comments:
                  type: array
                  items:
                    type: object
                fileIds:
                  type: array
                  items:
                    type: string
                sortOrder:
                  type: integer
      responses:
        "201":
          description: Task created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  task:
                    $ref: "#/components/schemas/Task"
  /api/tasks/{id}:
    get:
      tags:
        - Tasks
      summary: Get a single task by id
      x-app-scope: tasks:read
      description: |
        Returns 404 (never 403) when the task exists but lives in a project the caller cannot see — a 403 would confirm the id exists.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: The task
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  task:
                    $ref: "#/components/schemas/Task"
        "404":
          description: Not found, or not visible to the caller
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      tags:
        - Tasks
      summary: Update a task
      x-app-scope: tasks:write
      description: |
        Input keys are accepted in camelCase or snake_case (`dueDate` / `due_date`); camelCase wins when both are present. Responses are snake_case. Omitting a field leaves it unchanged; where noted, `null` clears it. `kvn_complete` is a generated column and is never writable.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                description:
                  type: string
                statusId:
                  type: string
                tag:
                  type: string
                priority:
                  type: string
                  enum:
                    - Normal
                    - Medium
                    - High
                    - Done
                  description: Matched case-insensitively; stored in canonical casing.
                stage:
                  type: string
                  enum:
                    - fire
                    - green
                    - yellow
                    - investigate
                    - plan
                    - implement
                  nullable: true
                  description: "`null` clears the stage."
                emoji:
                  type: string
                  maxLength: 16
                  nullable: true
                  description: Single-emoji label; `null` or empty clears it.
                assignedTo:
                  oneOf:
                    - type: string
                    - type: array
                      items:
                        type: string
                dueDate:
                  type: string
                  format: date
                  nullable: true
                startDate:
                  type: string
                  format: date
                  nullable: true
                  description: Scheduled start (Timeline/Gantt bar).
                isMilestone:
                  type: boolean
                reminderDate:
                  type: string
                  nullable: true
                createdBy:
                  type: string
                sortOrder:
                  type: integer
                projectRefs:
                  type: array
                  items:
                    type: string
                  description: |
                    Replace the full set of projects this task belongs to. The primary project (or `primaryProjectRef`) must be included.
                primaryProjectRef:
                  type: string
                parentTaskId:
                  type: integer
                  nullable: true
                  description: Re-parent this task; `null` detaches it.
                departmentId:
                  type: string
                  nullable: true
                  description: Owning department UUID; `null` clears it.
                location:
                  type: object
                  nullable: true
                  description: Map pin; `null` clears it.
                  properties:
                    lat:
                      type: number
                      minimum: -90
                      maximum: 90
                    lng:
                      type: number
                      minimum: -180
                      maximum: 180
                    address:
                      type: string
                      maxLength: 500
                expectedVersion:
                  type: integer
                  description: Optimistic-concurrency guard; mismatch returns 409.
                knowHow:
                  type: string
                vision:
                  type: string
                negation:
                  type: string
                visionRestated:
                  type: string
                negationSurfaced:
                  type: string
                subtasks:
                  type: array
                  items:
                    type: object
                notes:
                  type: array
                  items:
                    type: string
                links:
                  type: array
                  items:
                    type: object
                comments:
                  type: array
                  items:
                    type: object
                fileIds:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Task updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  task:
                    $ref: "#/components/schemas/Task"
        "404":
          description: Task not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      tags:
        - Tasks
      summary: Delete a task
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Task deleted
        "404":
          description: Task not found
  /api/tasks/{id}/archive:
    post:
      tags:
        - Tasks
      summary: Archive or restore a task in place
      description: |
        Non-destructive hide: an archived task keeps its id, comments, and history but is excluded from task list queries unless `includeArchived=true` is passed to GET /api/tasks. Send `{"archived": false}` to restore. Requires the tasks/edit permission (unlike delete, nothing is lost).
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                archived:
                  type: boolean
                  default: true
                  description: true archives the task, false restores it.
      responses:
        "200":
          description: Archive state updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  task:
                    $ref: "#/components/schemas/Task"
        "404":
          description: Task not found
  /api/tasks/reorder:
    post:
      tags:
        - Tasks
      summary: Reorder tasks (bulk status + sort update)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tasks
              properties:
                tasks:
                  type: array
                  items:
                    type: object
                    required:
                      - id
                      - statusId
                      - sortOrder
                      - projectRef
                    properties:
                      id:
                        type: integer
                      statusId:
                        type: string
                      sortOrder:
                        type: integer
                      projectRef:
                        type: string
      responses:
        "200":
          description: Tasks reordered
  /api/tasks/import:
    post:
      tags:
        - Tasks
      summary: Import tasks from CSV
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - csv
              properties:
                projectRef:
                  type: string
                csv:
                  type: string
      responses:
        "200":
          description: Tasks imported
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  importedCount:
                    type: integer
                  skippedRowCount:
                    type: integer
  /api/tasks/export:
    get:
      tags:
        - Tasks
      summary: Export a project's tasks as CSV, or the full board as JSON
      parameters:
        - name: projectRef
          in: query
          required: true
          schema:
            type: string
        - name: projectName
          in: query
          schema:
            type: string
        - name: format
          in: query
          description: CSV variant (standard/jira/asana) or `json` for a structured board export (project + ordered columns, each carrying its tasks).
          schema:
            type: string
            enum:
              - standard
              - jira
              - asana
              - json
      responses:
        "200":
          description: CSV file (CSV formats) or JSON board export (`format=json`)
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                type: object
  /api/clients:
    get:
      tags:
        - Clients
      summary: List all clients
      x-app-scope: clients:read
      responses:
        "200":
          description: Clients list with owner options
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  clients:
                    type: array
                    items:
                      $ref: "#/components/schemas/Client"
                  ownerOptions:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Clients
      summary: Create a new client
      x-app-scope: clients:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - company
                - contactName
                - email
              properties:
                company:
                  type: string
                contactName:
                  type: string
                email:
                  type: string
                  format: email
                contactRole:
                  type: string
                location:
                  type: string
                website:
                  type: string
                ownerId:
                  type: string
                ownerName:
                  type: string
                stage:
                  type: string
                health:
                  type: string
                invoiceStatus:
                  type: string
                arr:
                  oneOf:
                    - type: string
                    - type: number
                priority:
                  type: boolean
                archived:
                  type: boolean
      responses:
        "201":
          description: Client created
  /api/clients/{id}:
    get:
      tags:
        - Clients
      summary: Get a client by ID
      x-app-scope: clients:read
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Client details
        "404":
          description: Client not found
    patch:
      tags:
        - Clients
      summary: Update a client
      x-app-scope: clients:write
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Client updated
        "404":
          description: Client not found
    delete:
      tags:
        - Clients
      summary: Delete a client
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Client deleted
        "404":
          description: Client not found
  /api/clients/{id}/portal-access:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Clients
      summary: List portal members for a client
      responses:
        "200":
          description: Portal members list
    post:
      tags:
        - Clients
      summary: Grant or update portal access for a client employee
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - memberId
              properties:
                email:
                  type: string
                memberId:
                  type: string
                name:
                  type: string
                role:
                  type: string
                portalEnabled:
                  type: boolean
                canMessage:
                  type: boolean
                confirmReinvite:
                  type: boolean
      responses:
        "200":
          description: Portal member created or updated
        "409":
          description: Member was previously revoked; reinvite confirmation required
    delete:
      tags:
        - Clients
      summary: Revoke portal access for a client employee
      parameters:
        - name: memberId
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Portal access revoked
        "404":
          description: Portal member not found
  /api/clients/{id}/portal-access/{memberId}:
    patch:
      tags:
        - Clients
      summary: Update a portal member's access
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: memberId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                portalEnabled:
                  type: boolean
                canMessage:
                  type: boolean
      responses:
        "200":
          description: Portal member updated
  /api/projects:
    get:
      tags:
        - Projects
      summary: Get workspace project bundle
      x-app-scope: projects:read
      responses:
        "200":
          description: Project workspace bundle
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  bundle:
                    $ref: "#/components/schemas/ProjectWorkspaceBundle"
                  mode:
                    type: string
    post:
      tags:
        - Projects
      summary: Create a project
      x-app-scope: projects:write
      description: Creates a single project. Only `name` is required — the 3-step UI wizard asks for a project type, but that is a UI affordance, not a data requirement. Honours `Idempotency-Key`.
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          schema:
            type: string
          description: Opaque key; a retry replays the original response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Project name. The only required field.
                projectType:
                  type: string
                description:
                  type: string
                clientId:
                  type:
                    - string
                    - "null"
                startDate:
                  type:
                    - string
                    - "null"
                  description: Calendar date (YYYY-MM-DD).
                deadline:
                  type:
                    - string
                    - "null"
                  description: Calendar date (YYYY-MM-DD).
                visibility:
                  type: string
                  enum:
                    - public
                    - private
                members:
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      email:
                        type: string
                      team:
                        type: string
      responses:
        "201":
          description: Project created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  project:
                    type: object
                  bundle:
                    $ref: "#/components/schemas/ProjectWorkspaceBundle"
        "400":
          description: Invalid body
        "402":
          description: Plan project limit reached
    patch:
      tags:
        - Projects
      summary: Update workspace project bundle
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectWorkspaceBundle"
      responses:
        "200":
          description: Bundle updated
  /api/projects/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
    get:
      tags:
        - Projects
      summary: Get a single project
      x-app-scope: projects:read
      responses:
        "200":
          description: Project
        "404":
          description: Project not found
    delete:
      tags:
        - Projects
      summary: Delete a project and its resources
      responses:
        "200":
          description: Project deleted
        "404":
          description: Project not found
  /api/messages:
    get:
      tags:
        - Messages
      summary: Get all messages, contacts, and preferences
      responses:
        "200":
          description: Messages payload
    patch:
      tags:
        - Messages
      summary: Update chat preferences
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                preferences:
                  type: object
                  properties:
                    activeStatus:
                      type: boolean
                    notificationSound:
                      type: boolean
                    doNotDisturb:
                      type: boolean
      responses:
        "200":
          description: Preferences updated
  /api/messages/groups:
    post:
      tags:
        - Messages
      summary: Create a group chat
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                memberIds:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Group created
  /api/messages/groups/{groupId}/members:
    parameters:
      - name: groupId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Messages
      summary: Add a member to a group chat
      description: Adds a workspace member to a custom group chat. The caller must be a current group member; the target must be an active member of the workspace. Team-kind groups mirror workspace teams and reject membership edits. Adding an existing member is an idempotent no-op.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
              properties:
                userId:
                  type: string
      responses:
        "201":
          description: Member added
        "400":
          description: Missing/invalid userId, or target not an active workspace member
        "403":
          description: Caller may not manage this group's membership
        "404":
          description: Group not found in this workspace
    delete:
      tags:
        - Messages
      summary: Leave a group chat or remove a member
      description: With no body (or your own userId), leaves the group — always allowed on custom groups. With another user's id (JSON body `userId` or the `userId` query parameter), removes that member — workspace admins or the group creator only. The removed user's thread history is preserved. Team-kind groups reject membership edits.
      parameters:
        - name: userId
          in: query
          required: false
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
      responses:
        "200":
          description: Membership removed (idempotent)
        "403":
          description: Caller may not remove that member
        "404":
          description: Group not found in this workspace
  /api/messages/threads/{contactId}:
    patch:
      tags:
        - Messages
      summary: Update thread flags (pin, mute, block, etc.)
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                pinned:
                  type: boolean
                muted:
                  type: boolean
                blocked:
                  type: boolean
                archived:
                  type: boolean
                hidden:
                  type: boolean
      responses:
        "200":
          description: Thread updated
    delete:
      tags:
        - Messages
      summary: Delete a conversation thread
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Thread deleted
  /api/messages/threads/{contactId}/members:
    get:
      tags:
        - Messages
      summary: List the members of a group-type chat (team chat, group, or channel)
      description: Returns who is in a workspace team chat, managed group chat, or channel. The caller must be a participant (private channels included). Direct-message threads return 404 — they have presence, not a member list.
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Members of the chat, caller first, then alphabetical
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  members:
                    type: array
                    items:
                      type: object
                      properties:
                        userId:
                          type: string
                        name:
                          type: string
                        isYou:
                          type: boolean
                  canAdd:
                    type: boolean
                    description: Caller may add members via the chat's members route
                  canRemoveOthers:
                    type: boolean
                    description: Caller may remove other members
                  canLeave:
                    type: boolean
                    description: Caller may leave this chat
        "403":
          description: Caller is not a participant of this chat
        "404":
          description: Not a group-type chat, or not in this workspace
  /api/messages/threads/{contactId}/messages:
    post:
      tags:
        - Messages
      summary: Send a message
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: string
                attachments:
                  type: array
                  items:
                    type: object
      responses:
        "200":
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    $ref: "#/components/schemas/ChatMessage"
  /api/messages/threads/{contactId}/messages/{messageId}:
    patch:
      tags:
        - Messages
      summary: Edit a message
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
        - name: messageId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: string
      responses:
        "200":
          description: Message edited
    delete:
      tags:
        - Messages
      summary: Delete a message
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
        - name: messageId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Message deleted
  /api/messages/threads/{contactId}/messages/{messageId}/reactions:
    post:
      tags:
        - Messages
      summary: Toggle an emoji reaction on a message
      description: Toggles the caller's reaction for the given emoji — adds it when absent, removes it when present. The change fans out to every participant's copy of the message, so all participants see the same attributed reaction list. The caller must be a participant of the thread. Reactions are returned as `[{emoji, userIds}]`; a message carries at most 20 distinct emoji.
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
        - name: messageId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - emoji
              properties:
                emoji:
                  type: string
                  description: A single emoji (one grapheme, max 16 characters).
      responses:
        "200":
          description: Reaction toggled; returns the updated reaction list
        "400":
          description: Invalid emoji, or the distinct-reaction limit was reached
        "404":
          description: Message or conversation not found
  /api/messages/threads/{contactId}/typing:
    patch:
      tags:
        - Messages
      summary: Set typing state
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                typing:
                  type: boolean
      responses:
        "200":
          description: Typing state updated
  /api/messages/threads/{contactId}/meeting:
    get:
      tags:
        - Meetings
      summary: Get meeting session
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Meeting session data
    patch:
      tags:
        - Meetings
      summary: Update meeting controls or end/leave meeting
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                micEnabled:
                  type: boolean
                cameraEnabled:
                  type: boolean
                speakerEnabled:
                  type: boolean
                screenSharing:
                  type: boolean
                ended:
                  type: boolean
                left:
                  type: boolean
      responses:
        "200":
          description: Meeting updated
  /api/messages/threads/{contactId}/meeting/messages:
    post:
      tags:
        - Meetings
      summary: Send a meeting chat message
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                content:
                  type: string
      responses:
        "200":
          description: Meeting message sent
  /api/messages/threads/{contactId}/meeting/participants:
    post:
      tags:
        - Meetings
      summary: Add a meeting participant
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - participantId
              properties:
                participantId:
                  type: string
      responses:
        "200":
          description: Participant added
    patch:
      tags:
        - Meetings
      summary: Update a meeting participant (mute, kick)
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - participantId
              properties:
                participantId:
                  type: string
                micEnabled:
                  type: boolean
                kicked:
                  type: boolean
      responses:
        "200":
          description: Participant updated
  /api/messages/threads/{contactId}/meeting/signals:
    get:
      tags:
        - Meetings
      summary: Poll for WebRTC signals
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
        - name: after
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Signal list
    post:
      tags:
        - Meetings
      summary: Send a WebRTC signal
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - targetUserId
                - type
                - payload
              properties:
                targetUserId:
                  type: string
                type:
                  type: string
                  enum:
                    - offer
                    - answer
                    - ice-candidate
                payload:
                  type: object
      responses:
        "200":
          description: Signal sent
  /api/people:
    get:
      tags:
        - People
      summary: Get teams, members, and projects
      responses:
        "200":
          description: People workspace bundle
    patch:
      tags:
        - People
      summary: Update teams and members
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                teams:
                  type: array
                  items:
                    type: object
                members:
                  type: array
                  items:
                    type: object
                projects:
                  type: array
                  items:
                    type: object
      responses:
        "200":
          description: People bundle updated
  /api/notifications:
    get:
      tags:
        - Notifications
      summary: Get notification center data
      responses:
        "200":
          description: Notifications data
    patch:
      tags:
        - Notifications
      summary: Update notification state
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  required:
                    - action
                    - projectRef
                    - notificationId
                    - unread
                  properties:
                    action:
                      const: set-read
                    projectRef:
                      type: string
                    notificationId:
                      type: string
                    unread:
                      type: boolean
                - type: object
                  required:
                    - action
                    - projectRef
                    - notificationId
                    - snoozed
                  properties:
                    action:
                      const: set-snoozed
                    projectRef:
                      type: string
                    notificationId:
                      type: string
                    snoozed:
                      type: boolean
                - type: object
                  required:
                    - action
                    - projectRef
                    - notificationId
                  properties:
                    action:
                      const: remove
                    projectRef:
                      type: string
                    notificationId:
                      type: string
                - type: object
                  required:
                    - action
                  properties:
                    action:
                      const: mark-all-read
      responses:
        "200":
          description: Notification state updated
  /api/discussions:
    get:
      tags:
        - Discussions
      summary: Get discussions for a project
      x-app-scope: messages:read
      parameters:
        - name: projectRef
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Discussions list
    post:
      tags:
        - Discussions
      summary: Create a thread or reply (multipart form or JSON)
      x-app-scope: messages:write
      description: "Use multipart/form-data to attach files; application/json is accepted for text-only posts. When `mode` is omitted it is inferred: a body carrying `threadId` is a reply, one carrying `title` is a new thread."
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - mode
                - projectRef
              properties:
                mode:
                  type: string
                  enum:
                    - thread
                    - reply
                projectRef:
                  type: string
                title:
                  type: string
                body:
                  type: string
                threadId:
                  type: string
                content:
                  type: string
                authorName:
                  type: string
                authorInitials:
                  type: string
                authorTone:
                  type: string
                attachments:
                  type: array
                  items:
                    type: string
                    format: binary
          application/json:
            schema:
              type: object
              required:
                - projectRef
              properties:
                mode:
                  type: string
                  enum:
                    - thread
                    - reply
                projectRef:
                  type: string
                title:
                  type: string
                body:
                  type: string
                threadId:
                  type: string
                content:
                  type: string
                authorName:
                  type: string
                authorInitials:
                  type: string
                authorTone:
                  type: string
      responses:
        "201":
          description: Discussion created
        "400":
          description: Unparseable body, missing projectRef, or invalid write mode
    patch:
      tags:
        - Discussions
      summary: Toggle star or add reaction
      x-app-scope: messages:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum:
                    - toggle-star
                    - add-reaction
                projectRef:
                  type: string
                threadId:
                  type: string
                starred:
                  type: boolean
                targetType:
                  type: string
                  enum:
                    - thread
                    - reply
                targetId:
                  type: string
                emoji:
                  type: string
      responses:
        "200":
          description: Discussion updated
    delete:
      tags:
        - Discussions
      summary: Delete a thread or reply
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - targetType
                - targetId
              properties:
                projectRef:
                  type: string
                targetType:
                  type: string
                  enum:
                    - thread
                    - reply
                targetId:
                  type: string
      responses:
        "200":
          description: Discussion entry deleted
  /api/time-tracker:
    get:
      tags:
        - Time Tracker
      summary: Get time tracker dashboard
      x-app-scope: time_entries:read
      parameters:
        - name: actorName
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Time tracker dashboard
    post:
      tags:
        - Time Tracker
      summary: Start a timer
      x-app-scope: time_entries:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
                - projectRef
              properties:
                action:
                  const: start
                projectRef:
                  type: string
                taskId:
                  type: integer
                  nullable: true
                actorName:
                  type: string
      responses:
        "201":
          description: Timer started
    patch:
      tags:
        - Time Tracker
      summary: Stop a timer or update a time entry
      x-app-scope: time_entries:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
              properties:
                action:
                  type: string
                  enum:
                    - stop
                    - update
                entryId:
                  type: string
                actorName:
                  type: string
                projectRef:
                  type: string
                taskId:
                  type: integer
                  nullable: true
                startedAt:
                  type: string
                endedAt:
                  type: string
                  nullable: true
                billable:
                  type: boolean
                  description: Mark the entry billable (action=update only).
                hourlyRateCents:
                  type: integer
                  nullable: true
                  description: Per-entry hourly rate override in integer cents (action=update only). null clears the override so the project/client default applies. Entries already stamped onto an invoice reject updates.
      responses:
        "200":
          description: Timer stopped / entry updated
    delete:
      tags:
        - Time Tracker
      summary: Delete a time entry
      parameters:
        - name: entryId
          in: query
          required: true
          schema:
            type: string
        - name: actorName
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Time entry deleted
        "400":
          description: entryId is required
  /api/time-tracker/billing/preview:
    get:
      tags:
        - Time Tracker
      summary: Preview unbilled billable time for a client as invoice line items
      description: "Admin only. Rolls a client's unbilled billable time entries into ClearBooks-ready line items grouped per project (rate resolution: entry override → project default → client default; entries with no resolvable rate or still-running timers are returned as skipped with a reason). Read-only — nothing is created or stamped. Amounts are integer cents."
      parameters:
        - name: clientId
          in: query
          required: true
          schema:
            type: string
        - name: from
          in: query
          description: ISO timestamp lower bound on started_at (inclusive).
          schema:
            type: string
        - name: to
          in: query
          description: ISO timestamp upper bound on started_at (inclusive).
          schema:
            type: string
        - name: projectRefs
          in: query
          description: Comma-separated subset of the client's project refs.
          schema:
            type: string
      responses:
        "200":
          description: Billable-time preview (line items, total, skipped entries)
        "400":
          description: Unknown client / project refs not belonging to the client
  /api/time-tracker/billing/invoice:
    post:
      tags:
        - Time Tracker
      summary: Create a draft ClearBooks invoice from unbilled billable time
      description: Admin only. Creates a DRAFT invoice in ClearBooks (never sends) from the same selection the preview shows, then stamps the billed entries with the invoice reference. Only unbilled entries are ever selected, so a retry cannot double-bill. Requires the calling admin's ClearBooks connection and a client → ClearBooks-customer mapping (stored on the client, or passed as clearbooksCustomerId — an explicit id is remembered on the client when it has no mapping yet).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - clientId
              properties:
                clientId:
                  type: string
                from:
                  type: string
                to:
                  type: string
                projectRefs:
                  type: array
                  items:
                    type: string
                clearbooksCustomerId:
                  type: string
                invoiceDate:
                  type: string
                  description: YYYY-MM-DD; ClearBooks defaults it server-side.
                dueDate:
                  type: string
                  description: YYYY-MM-DD; ClearBooks defaults it server-side.
                notes:
                  type: string
      responses:
        "201":
          description: Draft created; returns invoiceId + stamped entry count
        "400":
          description: Nothing to bill / missing customer mapping / not connected
        "502":
          description: ClearBooks rejected the invoice, or the draft was created but stamping failed (the response carries invoiceId so the draft is not orphaned silently)
  /api/time-tracker/billing/unbill:
    post:
      tags:
        - Time Tracker
      summary: Clear the invoice stamp from time entries (local inverse)
      description: Admin only. Clears invoice_ref/invoiced_at from every entry stamped with the given ClearBooks invoice reference so the time becomes billable again — the local inverse for a voided/deleted draft. Does NOT touch ClearBooks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - invoiceRef
              properties:
                invoiceRef:
                  type: string
      responses:
        "200":
          description: Stamp cleared; returns clearedEntryCount
        "404":
          description: No entries carry that invoice reference
  /api/time-tracker/billing/rates:
    get:
      tags:
        - Time Tracker
      summary: Read default billing rates and the ClearBooks customer mapping
      description: Admin only. Returns every per-project default hourly rate in the workspace and, when clientId is passed, that client's default rate and ClearBooks customer mapping.
      parameters:
        - name: clientId
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Billing rate settings
    patch:
      tags:
        - Time Tracker
      summary: Set default billing rates / the ClearBooks customer mapping
      description: Admin only. Partial update — only fields present in the body are written. Rates are positive integer cents; null clears a rate (or the mapping).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                clientId:
                  type: string
                clientDefaultHourlyRateCents:
                  type: integer
                  nullable: true
                clearbooksCustomerId:
                  type: string
                  nullable: true
                projectRates:
                  type: array
                  items:
                    type: object
                    required:
                      - projectRef
                    properties:
                      projectRef:
                        type: string
                      defaultHourlyRateCents:
                        type: integer
                        nullable: true
      responses:
        "200":
          description: Updated settings summary
        "400":
          description: Invalid rate / no fields to update
        "404":
          description: Client not found
  /api/project-files:
    get:
      tags:
        - Files
      summary: List project files
      x-app-scope: files:read
      parameters:
        - name: projectRef
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: File list
    post:
      tags:
        - Files
      summary: Upload files to a project
      x-app-scope: files:write
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - projectRef
                - files
              properties:
                projectRef:
                  type: string
                documentId:
                  type: string
                files:
                  type: array
                  items:
                    type: string
                    format: binary
      responses:
        "201":
          description: Files uploaded
    patch:
      tags:
        - Files
      summary: Rename or duplicate a file
      x-app-scope: files:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
                - projectRef
                - fileId
              properties:
                action:
                  type: string
                  enum:
                    - rename
                    - duplicate
                projectRef:
                  type: string
                fileId:
                  type: string
                name:
                  type: string
      responses:
        "200":
          description: File updated
    delete:
      tags:
        - Files
      summary: Delete a project file
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - fileId
              properties:
                projectRef:
                  type: string
                fileId:
                  type: string
      responses:
        "200":
          description: File deleted
  /api/activity:
    get:
      tags:
        - Activity
      summary: Get workspace activity feed
      responses:
        "200":
          description: Activity feed
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  activity:
                    type: array
                    items:
                      $ref: "#/components/schemas/ActivityItem"
    post:
      tags:
        - Activity
      summary: Append an activity item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - item
              properties:
                item:
                  $ref: "#/components/schemas/ActivityItem"
      responses:
        "200":
          description: Activity appended
  /api/profile:
    get:
      tags:
        - Profile
      summary: Get current user profile
      responses:
        "200":
          description: User profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  profile:
                    $ref: "#/components/schemas/ProfileFormState"
    patch:
      tags:
        - Profile
      summary: Update user profile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProfileFormState"
      responses:
        "200":
          description: Profile updated
  /api/settings:
    get:
      tags:
        - Settings
      summary: Get user settings bundle
      responses:
        "200":
          description: Settings bundle
    patch:
      tags:
        - Settings
      summary: Update user settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Settings updated
  /api/settings/password:
    get:
      tags:
        - Settings
      summary: Whether the account has a password set
      description: Returns `{ hasPassword }`. False for OAuth-only accounts, which use this to offer setting a password without a current one.
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Settings
      summary: Change password
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - currentPassword
                - newPassword
                - confirmPassword
              properties:
                currentPassword:
                  type: string
                newPassword:
                  type: string
                  minLength: 8
                confirmPassword:
                  type: string
      responses:
        "200":
          description: Password changed
        "401":
          description: Current password incorrect
  /api/settings/account:
    delete:
      tags:
        - Settings
      summary: Delete user account
      responses:
        "200":
          description: Account deleted
  /api/settings/workspace:
    delete:
      tags:
        - Settings
      summary: Delete workspace (owner only)
      responses:
        "200":
          description: Workspace deleted
        "403":
          description: Not the workspace owner
  /api/settings/security:
    get:
      tags:
        - Settings
      summary: Get security info (active sessions)
      responses:
        "200":
          description: Security info
    post:
      tags:
        - Settings
      summary: Security action (e.g. revoke session)
      responses:
        "200":
          description: Action completed
  /api/settings/extension-shield:
    get:
      tags:
        - Settings
      summary: Is Extension Shield on for this workspace
      description: |
        Whether the workspace has the `extension_shield_enabled` security policy turned on. Read-only and visible to every member — the shield is a client-side containment mode each member's browser runs, not an admin secret. Writing the policy goes through `POST /api/admin/security/policies` (admin only).
      responses:
        "200":
          description: Shield state
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  enabled:
                    type: boolean
  /api/settings/trash:
    get:
      tags:
        - Settings
      summary: List trashed items
      responses:
        "200":
          description: Trash items
    post:
      tags:
        - Settings
      summary: Restore a trashed item
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  const: restore
                trashId:
                  type: string
      responses:
        "200":
          description: Item restored
    delete:
      tags:
        - Settings
      summary: Permanently delete a trashed item
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                trashId:
                  type: string
      responses:
        "200":
          description: Item permanently deleted
  /api/auth/workspace-setup:
    get:
      tags:
        - Auth
      summary: Get workspace setup state
      responses:
        "200":
          description: Setup state
        "401":
          description: Not authenticated
    post:
      tags:
        - Auth
      summary: Complete workspace setup
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                workspaceName:
                  type: string
                invites:
                  type: array
                  items:
                    type: string
                    format: email
      responses:
        "200":
          description: Setup completed
  /api/knowledge/documents:
    get:
      tags:
        - Knowledge
      summary: List knowledge documents
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 1000
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
      responses:
        "200":
          description: Documents
    post:
      tags:
        - Knowledge
      summary: Create a knowledge document
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
              properties:
                title:
                  type: string
                body:
                  type: string
                tags:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Created
  /api/knowledge/documents/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Knowledge
      summary: Get a knowledge document
      responses:
        "200":
          description: Document
        "404":
          description: Not found
    patch:
      tags:
        - Knowledge
      summary: Update a knowledge document
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                body:
                  type: string
                tags:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Updated
        "404":
          description: Not found
        "409":
          description: Optimistic-lock conflict
    delete:
      tags:
        - Knowledge
      summary: Delete a knowledge document (moves to trash)
      responses:
        "200":
          description: Deleted
  /api/knowledge/decisions:
    get:
      tags:
        - Knowledge
      summary: List decisions (ADR-style)
      responses:
        "200":
          description: Decisions
    post:
      tags:
        - Knowledge
      summary: Record a decision
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
              properties:
                title:
                  type: string
                refCode:
                  type: string
                status:
                  type: string
                  enum:
                    - active
                    - proposed
                    - superseded
                decidedAt:
                  type: string
                context:
                  type: string
                whatWeChose:
                  type: string
                tradeoffs:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Created
  /api/knowledge/decisions/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Knowledge
      responses:
        "200":
          description: Decision
    patch:
      tags:
        - Knowledge
      summary: Update a decision
      responses:
        "200":
          description: Updated
    delete:
      tags:
        - Knowledge
      summary: Delete a decision
      responses:
        "200":
          description: Deleted
  /api/knowledge/glossary:
    get:
      tags:
        - Knowledge
      summary: List glossary terms
      responses:
        "200":
          description: Terms
    post:
      tags:
        - Knowledge
      summary: Create a glossary term
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - term
              properties:
                term:
                  type: string
                domain:
                  type: string
                  enum:
                    - product
                    - tech
                    - process
                    - people
                definition:
                  type: string
                aliases:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Created
  /api/knowledge/glossary/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Knowledge
      summary: Get a glossary term
      responses:
        "200":
          description: Glossary term
    patch:
      tags:
        - Knowledge
      summary: Update a glossary term
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Updated
    delete:
      tags:
        - Knowledge
      summary: Delete a glossary term
      responses:
        "200":
          description: Deleted
  /api/knowledge/retros:
    get:
      tags:
        - Knowledge
      summary: List sprint retros
      responses:
        "200":
          description: Retros
    post:
      tags:
        - Knowledge
      summary: Create a sprint retro
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - sprintLabel
              properties:
                sprintLabel:
                  type: string
                status:
                  type: string
                  enum:
                    - draft
                    - ready
                    - shared
                windowStart:
                  type: string
                windowEnd:
                  type: string
                wins:
                  type: array
                  items:
                    type: object
                misses:
                  type: array
                  items:
                    type: object
                surprises:
                  type: array
                  items:
                    type: object
      responses:
        "201":
          description: Created
  /api/knowledge/retros/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Knowledge
      summary: Get a sprint retro
      responses:
        "200":
          description: Retro
    patch:
      tags:
        - Knowledge
      summary: Update a sprint retro
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Updated
    delete:
      tags:
        - Knowledge
      responses:
        "200":
          description: Deleted
  /api/knowledge/onboarding:
    get:
      tags:
        - Knowledge
      summary: List onboarding docs
      responses:
        "200":
          description: Onboarding docs
    post:
      tags:
        - Knowledge
      summary: Create or replace an onboarding doc for a role
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - role
              properties:
                role:
                  type: string
                persona:
                  type: string
                earlyWins:
                  type: array
                  items:
                    type: string
                thingsThatMatter:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Created
  /api/knowledge/onboarding/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Knowledge
      summary: Get an onboarding doc
      responses:
        "200":
          description: Onboarding doc
    patch:
      tags:
        - Knowledge
      summary: Update an onboarding doc
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Updated
    delete:
      tags:
        - Knowledge
      responses:
        "200":
          description: Deleted
  /api/calendar/export:
    get:
      tags:
        - Calendar
      summary: Export the workspace calendar as iCalendar
      description: Returns a `text/calendar` .ics attachment covering the full meeting history (paged internally). Requires the `export` capability.
      responses:
        "200":
          description: iCalendar file
          content:
            text/calendar:
              schema:
                type: string
        "401":
          description: Unauthorized
        "403":
          description: Missing export permission
        "404":
          description: No workspace found
  /api/calendar/tasks:
    get:
      tags:
        - Calendar
      summary: List visible tasks scheduled in a date window (inclusive)
      parameters:
        - name: from
          in: query
          required: true
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
        - name: to
          in: query
          required: true
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
      responses:
        "200":
          description: Calendar tasks
        "400":
          description: Invalid date window
  /api/support/tickets:
    get:
      tags:
        - Support
      summary: List support tickets
      parameters:
        - name: scope
          in: query
          description: "'mine' (default) or 'workspace' (admin/owner only)"
          schema:
            type: string
            enum:
              - mine
              - workspace
      responses:
        "200":
          description: Tickets
    post:
      tags:
        - Support
      summary: File a support ticket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - subject
                - description
              properties:
                subject:
                  type: string
                description:
                  type: string
                category:
                  type: string
                  enum:
                    - bug
                    - feature
                    - question
                    - billing
                    - general
                priority:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                    - urgent
                diagnostics:
                  type: object
      responses:
        "201":
          description: Ticket created
  /api/support/tickets/{ticketId}:
    parameters:
      - name: ticketId
        in: path
        required: true
        schema:
          type: integer
    patch:
      tags:
        - Support
      summary: Update a ticket's status and/or resolution note (admin/owner only)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: At least one of status or resolutionNote is required.
              properties:
                status:
                  type: string
                  enum:
                    - open
                    - in-progress
                    - resolved
                    - closed
                resolutionNote:
                  type: string
                  nullable: true
                  maxLength: 4000
                  description: What was done to resolve the ticket — shown to the reporter. Pass null to clear.
      responses:
        "200":
          description: Updated
        "403":
          description: Forbidden
        "404":
          description: Not found
  /api/recurring-tasks:
    get:
      tags:
        - RecurringTasks
      summary: List recurring-task generators for a project
      parameters:
        - name: projectRef
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Recurring tasks
    post:
      tags:
        - RecurringTasks
      summary: Create a recurring-task generator
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - title
                - recurrenceRule
              properties:
                projectRef:
                  type: string
                templateTaskId:
                  type: integer
                title:
                  type: string
                description:
                  type: string
                assignedTo:
                  type: string
                priority:
                  type: string
                tag:
                  type: string
                recurrenceRule:
                  type: string
                  description: iCal RRULE string
                nextDue:
                  type: string
                  format: date
                active:
                  type: boolean
      responses:
        "201":
          description: Created
  /api/recurring-tasks/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
    put:
      tags:
        - RecurringTasks
      summary: Update a recurring-task generator
      responses:
        "200":
          description: Updated
        "404":
          description: Not found
    delete:
      tags:
        - RecurringTasks
      summary: Delete a recurring-task generator
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/sla-policies:
    get:
      tags:
        - SLA
      summary: List SLA policies
      parameters:
        - name: projectRef
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Policies
    post:
      tags:
        - SLA
      summary: Create an SLA policy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - name
              properties:
                projectRef:
                  type: string
                name:
                  type: string
                priority:
                  type: string
                responseTimeMinutes:
                  type: integer
                  minimum: 1
                resolutionTimeMinutes:
                  type: integer
                  minimum: 1
                enabled:
                  type: boolean
      responses:
        "201":
          description: Created
  /api/sla-policies/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
    put:
      tags:
        - SLA
      summary: Update an SLA policy
      responses:
        "200":
          description: Updated
        "404":
          description: Not found
    delete:
      tags:
        - SLA
      summary: Delete an SLA policy
      responses:
        "200":
          description: Deleted
  /api/escalation-rules:
    get:
      tags:
        - Escalation
      summary: List escalation rules
      parameters:
        - name: projectRef
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Rules
    post:
      tags:
        - Escalation
      summary: Create an escalation rule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - name
                - conditionType
                - actionType
              properties:
                projectRef:
                  type: string
                name:
                  type: string
                conditionType:
                  type: string
                conditionConfig:
                  type: object
                actionType:
                  type: string
                actionConfig:
                  type: object
                enabled:
                  type: boolean
      responses:
        "201":
          description: Created
  /api/escalation-rules/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
    put:
      tags:
        - Escalation
      summary: Update an escalation rule
      responses:
        "200":
          description: Updated
        "404":
          description: Not found
    delete:
      tags:
        - Escalation
      summary: Delete an escalation rule
      responses:
        "200":
          description: Deleted
  /api/automations:
    get:
      tags:
        - Automations
      summary: List automation rules
      parameters:
        - name: projectRef
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Automations
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  automations:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Automations
      summary: Create an automation rule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectRef
                - name
                - triggerType
                - actionType
              properties:
                projectRef:
                  type: string
                name:
                  type: string
                triggerType:
                  type: string
                  description: One of created, moved, completed, assigned (see app_automations CHECK).
                triggerConfig:
                  type: object
                actionType:
                  type: string
                  description: One of assign, change-status, set-field, add-tag, remove-tag, add-comment, move-to-project, send-notification.
                actionConfig:
                  type: object
                enabled:
                  type: boolean
      responses:
        "201":
          description: Created
  /api/automations/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
    put:
      tags:
        - Automations
      summary: Update an automation rule
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                triggerType:
                  type: string
                triggerConfig:
                  type: object
                actionType:
                  type: string
                actionConfig:
                  type: object
                enabled:
                  type: boolean
      responses:
        "200":
          description: Updated
        "404":
          description: Not found
    delete:
      tags:
        - Automations
      summary: Delete an automation rule
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/automations/{id}/runs:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Automations
      summary: Recent execution history for one automation rule
      description: |
        Each row is one firing of the rule. Status is success, skipped, or error;
        detail is the action handler's diagnostic string.
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        "200":
          description: Runs
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  runs:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        automation_id:
                          type: string
                        workspace_id:
                          type: string
                          format: uuid
                        task_id:
                          type: integer
                          nullable: true
                        trigger_type:
                          type: string
                        status:
                          type: string
                          enum:
                            - success
                            - error
                            - skipped
                        detail:
                          type: string
                          nullable: true
                        ran_at:
                          type: string
                          format: date-time
  /api/projects/{id}/swimlanes:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Project ref (numeric id as string or slug).
    get:
      tags:
        - Swimlanes
      summary: List swimlanes on a project
      parameters:
        - name: includeArchived
          in: query
          description: Pass "1" to include archived lanes.
          schema:
            type: string
      responses:
        "200":
          description: Swimlanes
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  swimlanes:
                    type: array
                    items:
                      $ref: "#/components/schemas/Swimlane"
    post:
      tags:
        - Swimlanes
      summary: Create a swimlane on a project
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                position:
                  type: integer
                  minimum: 0
                  maximum: 9999
                  description: 0-indexed lane position. Omit to append.
                color:
                  type: string
                  nullable: true
                  maxLength: 32
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  swimlane:
                    $ref: "#/components/schemas/Swimlane"
  /api/swimlanes/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Swimlanes
      summary: Update a swimlane (name, position, color, archived)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                position:
                  type: integer
                  minimum: 0
                  maximum: 9999
                color:
                  type: string
                  nullable: true
                  maxLength: 32
                archived:
                  type: boolean
                  description: true archives the swimlane (sets archived_at), false restores it.
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  swimlane:
                    $ref: "#/components/schemas/Swimlane"
        "404":
          description: Not found or no changes
    delete:
      tags:
        - Swimlanes
      summary: Permanently delete a swimlane
      description: Tasks are not deleted, only the lane assignment is removed.
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/projects/{id}/column-limits:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Project ref.
    get:
      tags:
        - Board
      summary: List per-column WIP limits and current open-task counts
      responses:
        "200":
          description: Limits and counts
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  limits:
                    type: array
                    items:
                      $ref: "#/components/schemas/ColumnLimit"
                  counts:
                    type: object
                    additionalProperties:
                      type: integer
                    description: Map of statusId → open task count.
    put:
      tags:
        - Board
      summary: Upsert a WIP limit and/or column-name override for one status
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - statusId
              properties:
                statusId:
                  type: string
                wipLimit:
                  type: integer
                  nullable: true
                  minimum: 1
                  maximum: 1000
                  description: null clears the cap but keeps the row (for nameOverride).
                nameOverride:
                  type: string
                  nullable: true
                  maxLength: 64
      responses:
        "200":
          description: Upserted
    delete:
      tags:
        - Board
      summary: Remove the WIP limit and name override for one status
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - statusId
              properties:
                statusId:
                  type: string
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/projects/{id}/cycle-time:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Project ref.
    get:
      tags:
        - Reports
      summary: Per-status cycle-time stats for a project
      description: |
        Mean, median (p50), and p90 time tasks spent in each column over the last
        sinceDays days. Sourced from app_task_transitions (real column moves).
      parameters:
        - name: sinceDays
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 365
            default: 90
      responses:
        "200":
          description: Cycle-time rows
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  sinceDays:
                    type: integer
                  timings:
                    type: array
                    items:
                      $ref: "#/components/schemas/StatusTiming"
  /api/tasks/{id}/transitions:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
    get:
      tags:
        - Tasks
      summary: Move history for one task
      description: |
        Every column the task has lived in, who moved it, when, and how long it
        sat in the previous status. Append-only; never updated.
      responses:
        "200":
          description: Transitions
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  transitions:
                    type: array
                    items:
                      $ref: "#/components/schemas/TaskTransition"
        "404":
          description: Task not found
  /api/integrations:
    get:
      tags:
        - Integrations
      summary: List available integration providers and the user's connections
      responses:
        "200":
          description: Catalog + connection summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  providers:
                    type: array
                    items:
                      $ref: "#/components/schemas/IntegrationProvider"
                  connections:
                    type: array
                    items:
                      $ref: "#/components/schemas/IntegrationConnection"
  /api/integrations/{provider}:
    parameters:
      - name: provider
        in: path
        required: true
        schema:
          type: string
        description: Provider slug (e.g. `angelsend`, `gmail`, `google-drive`, `google-calendar`, `slack`, `asana`).
    get:
      tags:
        - Integrations
      summary: Get a single provider plus the caller's connection (if any)
      responses:
        "200":
          description: Provider + connection
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  provider:
                    $ref: "#/components/schemas/IntegrationProvider"
                  connection:
                    oneOf:
                      - $ref: "#/components/schemas/IntegrationConnection"
                      - type: "null"
        "404":
          description: Unknown provider
    put:
      tags:
        - Integrations
      summary: Store an API-key credential for an api_key provider
      description: |
        Only valid when the provider's `authType` is `api_key` (currently
        AngelSend). For OAuth providers use `/api/integrations/{provider}/oauth/start`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - apiKey
              properties:
                apiKey:
                  type: string
                  format: password
                metadata:
                  type: object
                  additionalProperties: true
                  description: Optional caller-supplied display metadata to merge alongside derived metadata.
      responses:
        "200":
          description: Connection saved
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  connection:
                    $ref: "#/components/schemas/IntegrationConnection"
        "400":
          description: Missing apiKey or wrong auth type
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Unknown provider
    delete:
      tags:
        - Integrations
      summary: Disconnect the caller from a provider
      responses:
        "200":
          description: Removed (`removed` is false if there was no row to begin with)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  removed:
                    type: boolean
  /api/integrations/{provider}/test:
    parameters:
      - name: provider
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Integrations
      summary: Probe the stored credential against the provider's identity endpoint
      description: |
        Calls the provider's "who am I" endpoint (AngelSend connection-status,
        Gmail profile, Drive about, Slack auth.test, Asana /users/me, Google
        Calendar calendarList) and updates the connection's status + metadata
        based on the result.
      responses:
        "200":
          description: Connection healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  result:
                    description: Raw provider response (shape varies by provider).
        "412":
          description: Not connected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "501":
          description: No test implemented for this provider
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Provider rejected the credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/integrations/{provider}/oauth/start:
    parameters:
      - name: provider
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: Begin an OAuth2 authorization-code flow
      description: |
        Generates a state token, sets it on an httpOnly cookie, then redirects
        the browser to the provider's authorize URL. Only valid for providers
        with `authType: oauth2`.
      responses:
        "302":
          description: Redirect to provider authorize URL
        "400":
          description: Provider does not use OAuth
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: OAuth client credentials not configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/integrations/{provider}/oauth/callback:
    parameters:
      - name: provider
        in: path
        required: true
        schema:
          type: string
      - name: code
        in: query
        schema:
          type: string
      - name: state
        in: query
        schema:
          type: string
      - name: error
        in: query
        schema:
          type: string
      - name: error_description
        in: query
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: OAuth2 callback (browser-only, exchanges code for tokens)
      description: |
        Verifies the state cookie, exchanges the code for an access/refresh
        token bundle, persists the encrypted bundle to the user's connection,
        then redirects back to `/settings?tab=integrations&oauth=<slug>&status=...`.
      responses:
        "302":
          description: Redirect back to Settings → Integrations with status flash
  /api/integrations/angelsend/calendar:
    get:
      tags:
        - Integrations
      summary: List events from the connected AngelSend calendar
      parameters:
        - name: from
          in: query
          schema:
            type: string
            format: date-time
          description: ISO timestamp window start (optional).
        - name: to
          in: query
          schema:
            type: string
            format: date-time
          description: ISO timestamp window end (optional).
      responses:
        "200":
          description: Events
        "412":
          description: AngelSend not connected
    post:
      tags:
        - Integrations
      summary: Create an event on the connected AngelSend calendar
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - start
                - end
              properties:
                title:
                  type: string
                summary:
                  type: string
                  description: Falls back to `title` if omitted.
                description:
                  type: string
                  nullable: true
                start:
                  type: string
                  format: date-time
                end:
                  type: string
                  format: date-time
                location:
                  type: string
                  nullable: true
                attendees:
                  type: array
                  items:
                    type: object
                    properties:
                      email:
                        type: string
                      name:
                        type: string
      responses:
        "200":
          description: Event created
        "400":
          description: Missing required fields
        "412":
          description: AngelSend not connected
  /api/integrations/angelsend/contacts:
    get:
      tags:
        - Integrations
      summary: List leads / contacts from AngelSend
      responses:
        "200":
          description: Contacts
        "412":
          description: AngelSend not connected
  /api/integrations/angelsend/credits:
    get:
      tags:
        - Integrations
      summary: Get the AngelSend credit balance for the connected account
      responses:
        "200":
          description: Balance
        "412":
          description: AngelSend not connected
  /api/integrations/angelsend/email:
    post:
      tags:
        - Integrations
      summary: Send email via AngelSend
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - to
                - subject
              properties:
                to:
                  oneOf:
                    - type: string
                    - type: array
                      items:
                        type: string
                subject:
                  type: string
                body:
                  type: string
                html:
                  type: string
                from:
                  type: string
                replyTo:
                  type: string
                cc:
                  type: array
                  items:
                    type: string
                bcc:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Sent
        "412":
          description: AngelSend not connected
  /api/integrations/gmail/profile:
    get:
      tags:
        - Integrations
      summary: Get the connected Gmail account's profile (email, message counts)
      responses:
        "200":
          description: Profile
        "412":
          description: Gmail not connected
  /api/integrations/gmail/messages:
    get:
      tags:
        - Integrations
      summary: List Gmail message ids (and threadIds)
      parameters:
        - name: q
          in: query
          schema:
            type: string
          description: Gmail search query syntax.
        - name: maxResults
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
        - name: pageToken
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Messages
        "412":
          description: Gmail not connected
    post:
      tags:
        - Integrations
      summary: Send a plain-text email through Gmail
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - to
              properties:
                to:
                  type: string
                subject:
                  type: string
                body:
                  type: string
                cc:
                  type: string
                bcc:
                  type: string
                replyTo:
                  type: string
      responses:
        "200":
          description: Sent
        "400":
          description: Missing to / subject / body
        "412":
          description: Gmail not connected
  /api/integrations/google-drive/about:
    get:
      tags:
        - Integrations
      summary: Get the Drive account's identity and storage quota
      responses:
        "200":
          description: About
        "412":
          description: Google Drive not connected
  /api/integrations/google-drive/files:
    get:
      tags:
        - Integrations
      summary: List Drive files visible to the app (drive.file scope)
      parameters:
        - name: q
          in: query
          schema:
            type: string
          description: Drive query syntax.
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
        - name: pageToken
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Files
        "412":
          description: Google Drive not connected
    post:
      tags:
        - Integrations
      summary: Create a UTF-8 text file in Drive
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                content:
                  type: string
                  description: UTF-8 file body. Defaults to empty string.
                mimeType:
                  type: string
                  description: Defaults to `text/plain; charset=UTF-8`.
                parentFolderId:
                  type: string
      responses:
        "200":
          description: File
        "400":
          description: Missing name
        "412":
          description: Google Drive not connected
  /api/integrations/google-calendar/calendars:
    get:
      tags:
        - Integrations
      summary: List the user's Google calendars
      responses:
        "200":
          description: Calendars
        "412":
          description: Google Calendar not connected
  /api/integrations/google-calendar/events:
    get:
      tags:
        - Integrations
      summary: List events from one of the user's Google calendars
      parameters:
        - name: calendarId
          in: query
          schema:
            type: string
          description: Defaults to `primary`.
        - name: timeMin
          in: query
          schema:
            type: string
            format: date-time
          description: Window start. Also accepts `from`.
        - name: timeMax
          in: query
          schema:
            type: string
            format: date-time
          description: Window end. Also accepts `to`.
        - name: from
          in: query
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          schema:
            type: string
            format: date-time
        - name: maxResults
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 2500
        - name: pageToken
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Events
        "412":
          description: Google Calendar not connected
    post:
      tags:
        - Integrations
      summary: Create an event on one of the user's Google calendars
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - summary
                - start
                - end
              properties:
                calendarId:
                  type: string
                  description: Defaults to `primary`.
                summary:
                  type: string
                description:
                  type: string
                location:
                  type: string
                start:
                  type: string
                  format: date-time
                end:
                  type: string
                  format: date-time
                timeZone:
                  type: string
                attendees:
                  type: array
                  items:
                    type: string
                sendUpdates:
                  type: string
                  enum:
                    - all
                    - externalOnly
                    - none
      responses:
        "200":
          description: Event created
        "400":
          description: Missing summary / start / end
        "412":
          description: Google Calendar not connected
  /api/integrations/slack/auth-test:
    get:
      tags:
        - Integrations
      summary: Run Slack's auth.test method (returns bot/user/team identity)
      responses:
        "200":
          description: Identity
        "412":
          description: Slack not connected
  /api/integrations/slack/channels:
    get:
      tags:
        - Integrations
      summary: List channels visible to the connected Slack bot
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
        - name: types
          in: query
          schema:
            type: string
          description: Slack `types` filter, e.g. `public_channel,private_channel`.
        - name: excludeArchived
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Channels
        "412":
          description: Slack not connected
  /api/integrations/slack/messages:
    post:
      tags:
        - Integrations
      summary: Post a message to a Slack channel via chat.postMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channel
                - text
              properties:
                channel:
                  type: string
                  description: Channel id (C…) or `#name`.
                text:
                  type: string
                threadTs:
                  type: string
      responses:
        "200":
          description: Posted
        "400":
          description: Missing channel / text
        "412":
          description: Slack not connected
    patch:
      tags:
        - Integrations
      summary: Edit a Slack message via chat.update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channel
                - ts
                - text
              properties:
                channel:
                  type: string
                ts:
                  type: string
                  description: Timestamp id of the message to edit.
                text:
                  type: string
      responses:
        "200":
          description: Updated
        "400":
          description: Missing channel / ts / text
    delete:
      tags:
        - Integrations
      summary: Delete a Slack message via chat.delete
      description: Accepts channel and ts either as query params or in a JSON body.
      parameters:
        - name: channel
          in: query
          schema:
            type: string
        - name: ts
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Deleted
        "400":
          description: Missing channel / ts
  /api/integrations/asana/users-me:
    get:
      tags:
        - Integrations
      summary: Get the connected Asana user's profile (name, email, workspaces)
      responses:
        "200":
          description: User
        "412":
          description: Asana not connected
  /api/integrations/asana/workspaces:
    get:
      tags:
        - Integrations
      summary: List Asana workspaces the connected account belongs to
      responses:
        "200":
          description: Workspaces
        "412":
          description: Asana not connected
  /api/integrations/asana/tasks:
    get:
      tags:
        - Integrations
      summary: List Asana tasks (filtered by workspace or project)
      parameters:
        - name: workspace
          in: query
          schema:
            type: string
          description: Workspace gid (required unless project is given).
        - name: project
          in: query
          schema:
            type: string
          description: Project gid.
        - name: assignee
          in: query
          schema:
            type: string
          description: User gid or `me`.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
      responses:
        "200":
          description: Tasks
        "400":
          description: Neither workspace nor project provided
        "412":
          description: Asana not connected
    post:
      tags:
        - Integrations
      summary: Create a task in an Asana workspace
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - workspace
                - name
              properties:
                workspace:
                  type: string
                name:
                  type: string
                notes:
                  type: string
                assignee:
                  type: string
                dueOn:
                  type: string
                  format: date
                projects:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Task
        "400":
          description: Missing workspace or name
        "412":
          description: Asana not connected
  /api/ai-agent:
    post:
      tags:
        - AI Agent
      summary: Send a chat message to the AI agent
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ai-agent/attachments:
    delete:
      tags:
        - AI Agent
      summary: Delete attachments
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - AI Agent
      summary: List attachments
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - AI Agent
      summary: Create attachment
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ai-agent/audit:
    get:
      tags:
        - AI Agent
      summary: List audit
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/config:
    get:
      tags:
        - AI Agent
      summary: List config
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - AI Agent
      summary: Update config
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/conversations:
    get:
      tags:
        - AI Agent
      summary: List conversations
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - AI Agent
      summary: Create conversation
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ai-agent/conversations/search:
    get:
      tags:
        - AI Agent
      summary: Search conversations
      description: Full-text search over the caller's own past agent conversations (message bodies and titles). Scoped to the authenticated (workspace, user).
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            maxLength: 300
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 25
      responses:
        "200":
          description: Success
        "400":
          description: Query too long
        "401":
          description: Unauthorized
        "403":
          description: AI agent disabled for this workspace
  /api/ai-agent/conversations/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - AI Agent
      summary: Delete conversation
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - AI Agent
      summary: Fetch conversation
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - AI Agent
      summary: Update conversation
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/conversations/{id}/export:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - AI Agent
      summary: List export
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/custom-tools:
    get:
      tags:
        - AI Agent
      summary: List custom tools
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - AI Agent
      summary: Create custom tool
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ai-agent/custom-tools/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - AI Agent
      summary: Delete custom tool
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - AI Agent
      summary: Update custom tool
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/feedback:
    delete:
      tags:
        - AI Agent
      summary: Delete feedback
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - AI Agent
      summary: Create feedback
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ai-agent/feedback/analytics:
    get:
      tags:
        - AI Agent
      summary: List analytics
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/jobs:
    get:
      tags:
        - AI Agent
      summary: List jobs
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/jobs/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - AI Agent
      summary: Fetch job
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/mention:
    post:
      tags:
        - AI Agent
      summary: Create mention
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ai-agent/preferences:
    get:
      tags:
        - AI Agent
      summary: List preferences
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - AI Agent
      summary: Update preferences
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/ai-agent/tools:
    get:
      tags:
        - AI Agent
      summary: List the agent tools the caller may execute
      description: |
        Filtered by persona scope, read-only action mode, data scopes and ADMIN_ONLY_TOOLS — this is the caller's effective toolset, not the raw registry.
      parameters:
        - name: persona_id
          in: query
          schema:
            type: string
          description: Evaluate the toolset as this persona.
        - name: q
          in: query
          schema:
            type: string
          description: Case-insensitive filter over name and description.
      responses:
        "200":
          description: Available tools
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  count:
                    type: integer
                  tools:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        description:
                          type: string
                        read_only:
                          type: boolean
                        category:
                          type: string
                          nullable: true
                          description: Action-policy category; null means not policy-gated.
                        source:
                          type: string
                          enum:
                            - builtin
                            - custom
                            - app
                        input_schema:
                          type: object
                          nullable: true
        "403":
          description: The AI agent is not enabled for this workspace
  /api/ai-agent/tools/{name}/execute:
    post:
      tags:
        - AI Agent
      summary: Execute one agent tool by name
      description: |
        Deterministic counterpart to POST /api/ai-agent. Applies the full governance stack: effective-toolset filtering, argument guardrails, the action policy (FAIL CLOSED — an `approve` verdict parks rather than runs, unlike interactive chat), the action ledger and undo recording.
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                input:
                  type: object
                  additionalProperties: true
                  description: Arguments for the tool, per its input_schema.
                persona_id:
                  type: string
                  description: Execute within this persona's scope.
                run_id:
                  type: string
                  description: |
                    Idempotency scope. Reusing it after a timeout returns the first result instead of repeating a risky write.
      responses:
        "200":
          description: Executed
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  status:
                    type: string
                    enum:
                      - executed
                  tool:
                    type: string
                  category:
                    type: string
                    nullable: true
                  content:
                    type: string
                  run_id:
                    type: string
                  undo_group:
                    type: object
                    nullable: true
                    properties:
                      id:
                        type: string
                      label:
                        type: string
                      count:
                        type: integer
        "202":
          description: |
            Parked for human approval (policy mode `approve`, or a daily budget exhausted). Nothing ran. See GET /api/ai-agent/approvals.
        "403":
          description: Blocked by a guardrail or by the workspace action policy
        "404":
          description: Unknown tool, or not available to this caller
        "500":
          description: The tool threw, or the approval row could not be recorded
  /api/ai-agent/undo:
    post:
      tags:
        - AI Agent
      summary: Create undo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/scim/logs:
    get:
      tags:
        - Admin
      summary: List logs
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/scim/providers:
    get:
      tags:
        - Admin
      summary: List providers
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create provider
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/scim/providers/{providerId}:
    parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete provider
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Admin
      summary: Fetch provider
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Admin
      summary: Update provider
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/scim/providers/{providerId}/mappings:
    parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Admin
      summary: List mappings
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create mapping
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/scim/providers/{providerId}/mappings/{mappingId}:
    parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
      - name: mappingId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete mapping
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/admin/scim/providers/{providerId}/resync:
    parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Admin
      summary: Create resync
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/scim/providers/{providerId}/tokens:
    parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Admin
      summary: List tokens
      description: Bearer tokens for this SCIM provider, newest first. Revoked tokens are included. The plaintext token is never returned — only its hash is stored.
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
        "404":
          description: Not found
    post:
      tags:
        - Admin
      summary: Create token
      description: Mint an additional bearer token for this provider (credential rotation). The plaintext `bearerToken` is returned exactly once and cannot be retrieved again.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                label:
                  type: string
      responses:
        "201":
          description: Created
        "401":
          description: Unauthorized
        "404":
          description: Not found
  /api/admin/scim/providers/{providerId}/tokens/{tokenId}:
    parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
      - name: tokenId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete token
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/record-shares:
    get:
      tags:
        - Clients
      summary: List record shares
      description: Who an account or deal is shared with, plus the caller's own access level and the pickable members/teams. Answers 404 when the caller cannot reach the record, so it can't be used to enumerate private records.
      parameters:
        - name: resourceType
          in: query
          required: true
          schema:
            type: string
            enum:
              - client
              - deal
        - name: resourceId
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "404":
          description: Not found
    post:
      tags:
        - Clients
      summary: Create record share
      description: Grant or update a share, and/or flip the record's visibility between 'workspace' and 'private'. Restricted to the record owner or a workspace admin — an `edit` share does NOT confer the right to re-share.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - resourceType
                - resourceId
              properties:
                resourceType:
                  type: string
                  enum:
                    - client
                    - deal
                resourceId:
                  type: string
                subjectType:
                  type: string
                  enum:
                    - user
                    - team
                subjectId:
                  type: string
                access:
                  type: string
                  enum:
                    - view
                    - edit
                visibility:
                  type: string
                  enum:
                    - workspace
                    - private
      responses:
        "200":
          description: Visibility updated
        "201":
          description: Share created
        "400":
          description: Invalid request
        "403":
          description: Not the owner or an admin
        "404":
          description: Not found
    delete:
      tags:
        - Clients
      summary: Delete record share
      parameters:
        - name: resourceType
          in: query
          required: true
          schema:
            type: string
            enum:
              - client
              - deal
        - name: resourceId
          in: query
          required: true
          schema:
            type: string
        - name: subjectType
          in: query
          required: true
          schema:
            type: string
            enum:
              - user
              - team
        - name: subjectId
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Removed
        "400":
          description: Invalid request
        "403":
          description: Not the owner or an admin
        "404":
          description: Not found
  /api/admin/security/domains:
    get:
      tags:
        - Admin
      summary: List domains
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create domain
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/security/domains/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete domain
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/admin/security/ip-allowlist:
    get:
      tags:
        - Admin
      summary: List ip allowlist
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create ip allowlist
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/security/ip-allowlist/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete ip allowlist
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/admin/security/policies:
    get:
      tags:
        - Admin
      summary: List policies
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create a security policy
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/security/sessions:
    get:
      tags:
        - Admin
      summary: List sessions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/security/sessions/{sessionId}:
    parameters:
      - name: sessionId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete session
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/admin/security/sso:
    get:
      tags:
        - Admin
      summary: List sso
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create sso
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super:
    get:
      tags:
        - Admin
      summary: List super
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/compliance-requests:
    get:
      tags:
        - Admin
      summary: List compliance requests
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Admin
      summary: Update compliance requests
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/data-governance:
    get:
      tags:
        - Admin
      summary: List data governance
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Admin
      summary: Update data governance
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/data-governance/export/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Admin
      summary: Fetch export
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/data-governance/pii:
    post:
      tags:
        - Admin
      summary: Create pii
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/data-governance/redact:
    post:
      tags:
        - Admin
      summary: Create redact
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/data-governance/requests/{requestId}:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Admin
      summary: Update request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/meta:
    get:
      tags:
        - Admin
      summary: List meta
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/settings:
    get:
      tags:
        - Admin
      summary: List settings
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Admin
      summary: Update settings
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/settings/logo:
    post:
      tags:
        - Admin
      summary: Create logo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/settings/test:
    post:
      tags:
        - Admin
      summary: Create test
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/support-inbox:
    get:
      tags:
        - Admin
      summary: List support inbox
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/support-inbox/submissions/{submissionId}:
    parameters:
      - name: submissionId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Admin
      summary: Update submission
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/support-inbox/tickets/{ticketId}:
    parameters:
      - name: ticketId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Admin
      summary: Update ticket
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/tooling:
    get:
      tags:
        - Admin
      summary: List tooling
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/tooling/billing:
    patch:
      tags:
        - Admin
      summary: Update billing
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/tooling/bulk-tasks:
    post:
      tags:
        - Admin
      summary: Create bulk task
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/tooling/config:
    get:
      tags:
        - Admin
      summary: List config
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Admin
      summary: Create config
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/tooling/feature-flags:
    delete:
      tags:
        - Admin
      summary: Delete feature flags
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Admin
      summary: Create feature flag
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/tooling/integrations:
    delete:
      tags:
        - Admin
      summary: Delete integrations
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Admin
      summary: Create integration
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/tooling/quotas:
    patch:
      tags:
        - Admin
      summary: Update quotas
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/tooling/sandboxes:
    delete:
      tags:
        - Admin
      summary: Delete a sandbox
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Admin
      summary: Create a sandbox
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/admin/super/users/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Admin
      summary: Delete user
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Admin
      summary: Update user
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/super/workspaces/{workspaceId}:
    parameters:
      - name: workspaceId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Admin
      summary: Update workspace
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/users/duplicates:
    get:
      tags:
        - Admin
      summary: List duplicates
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/admin/users/merge:
    post:
      tags:
        - Admin
      summary: Create merge
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/agent-alerts:
    get:
      tags:
        - Agent Alerts
      summary: List agent alerts
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Agent Alerts
      summary: Update agent alerts
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/agent-alerts/briefing:
    get:
      tags:
        - Agent Alerts
      summary: List briefing
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/agent-alerts/dismiss:
    get:
      tags:
        - Agent Alerts
      summary: List dismissed agent alerts
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Agent Alerts
      summary: Dismiss an agent alert
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/agent-alerts/preferences:
    get:
      tags:
        - Agent Alerts
      summary: List preferences
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Agent Alerts
      summary: Update preferences
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/agent-alerts/scan:
    post:
      tags:
        - Agent Alerts
      summary: Create scan
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/audit-logs:
    get:
      tags:
        - Audit Logs
      summary: List audit logs
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/audit-logs/alerts:
    delete:
      tags:
        - Audit Logs
      summary: Delete alerts
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Audit Logs
      summary: List alerts
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Audit Logs
      summary: Update alerts
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Audit Logs
      summary: Create alert
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/audit-logs/compliance:
    get:
      tags:
        - Audit Logs
      summary: List compliance
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/audit-logs/data-access:
    get:
      tags:
        - Audit Logs
      summary: List data access
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/audit-logs/event-config:
    get:
      tags:
        - Audit Logs
      summary: List event config
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Audit Logs
      summary: Create event config
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/audit-logs/export:
    get:
      tags:
        - Audit Logs
      summary: List export
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/audit-logs/retention:
    get:
      tags:
        - Audit Logs
      summary: List retention
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Audit Logs
      summary: Create retention
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/audit-logs/search:
    get:
      tags:
        - Audit Logs
      summary: List search
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/audit-logs/webhooks:
    delete:
      tags:
        - Audit Logs
      summary: Delete webhooks
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Audit Logs
      summary: List webhooks
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Audit Logs
      summary: Update webhooks
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Audit Logs
      summary: Create webhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/accounts/list:
    get:
      tags:
        - Auth
      summary: List linked accounts on this device
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/auth/accounts/remove:
    post:
      tags:
        - Auth
      summary: Unlink an account from this device
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/accounts/switch:
    post:
      tags:
        - Auth
      summary: Switch to another linked account
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/github/start:
    get:
      tags:
        - Auth
      summary: List start
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/auth/google/start:
    get:
      tags:
        - Auth
      summary: List start
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/auth/mfa/enroll:
    delete:
      tags:
        - Auth
      summary: Delete enroll
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Auth
      summary: List enroll
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Auth
      summary: Create enroll
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/mfa/recovery-codes:
    get:
      tags:
        - Auth
      summary: List recovery codes
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Auth
      summary: Create recovery code
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/mfa/verify:
    post:
      tags:
        - Auth
      summary: Create verify
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/oneprofile/start:
    get:
      tags:
        - Auth
      summary: List start
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/auth/refresh:
    post:
      tags:
        - Auth
      summary: Create refresh
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/resend-verification:
    post:
      tags:
        - Auth
      summary: Create resend verification
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/sso/oidc/callback:
    get:
      tags:
        - Auth
      summary: List callback
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/auth/sso/oidc/login:
    post:
      tags:
        - Auth
      summary: Create login
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/sso/saml/acs:
    post:
      tags:
        - Auth
      summary: SAML assertion consumer service (ACS) callback
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/sso/saml/login:
    post:
      tags:
        - Auth
      summary: Create login
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/sso/saml/metadata:
    get:
      tags:
        - Auth
      summary: List metadata
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/auth/verify-email:
    get:
      tags:
        - Auth
      summary: List verify email
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Auth
      summary: Create verify email
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/auth/workspace-selection:
    get:
      tags:
        - Auth
      summary: List workspace selection
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/automations/page-data:
    get:
      tags:
        - Automations
      summary: List page data
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/credits/summary:
    get:
      tags:
        - Billing
      summary: List summary
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/portal/auth/accept-invite:
    post:
      tags:
        - Client Portal
      summary: Create accept invite
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portal/auth/login:
    post:
      tags:
        - Client Portal
      summary: Create login
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portal/auth/logout:
    post:
      tags:
        - Client Portal
      summary: Create logout
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portal/decisions:
    get:
      tags:
        - Client Portal
      summary: List decisions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Client Portal
      summary: Create decision
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portal/discussions:
    post:
      tags:
        - Client Portal
      summary: Create discussion
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portal/export:
    get:
      tags:
        - Client Portal
      summary: List export
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/portal/files:
    post:
      tags:
        - Client Portal
      summary: Create file
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portal/files/{fileId}:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Client Portal
      summary: Fetch file
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/clients/{id}/employees:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Clients
      summary: Create employee
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/clients/{id}/employees/{employeeId}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: employeeId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Clients
      summary: Delete employee
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Clients
      summary: Update employee
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/clients/export:
    get:
      tags:
        - Clients
      summary: List export
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/communication:
    get:
      tags:
        - Communication
      summary: List communication
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Communication
      summary: Create communication
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/components:
    get:
      tags:
        - Components
      summary: List components
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Components
      summary: Create component
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/components/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Components
      summary: Delete component
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    put:
      tags:
        - Components
      summary: Update component
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/cron/ai-agent-jobs:
    get:
      tags:
        - Cron
      summary: List ai agent jobs
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Cron
      summary: Create ai agent job
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/ai-agent-retention:
    post:
      tags:
        - Cron
      summary: Create ai agent retention
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/audit-log-immutable-sink:
    post:
      tags:
        - Cron
      summary: Create audit log immutable sink
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/audit-log-retention:
    post:
      tags:
        - Cron
      summary: Create audit log retention
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/billing-dunning:
    post:
      tags:
        - Cron
      summary: Create billing dunning
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/billing-trial-expiration:
    post:
      tags:
        - Cron
      summary: Create billing trial expiration
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/daily-digest:
    post:
      tags:
        - Cron
      summary: Create daily digest
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/chat-events-prune:
    get:
      tags:
        - Cron
      summary: Run chat events prune (Vercel cron invokes crons with GET)
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Cron
      summary: Create chat events prune
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/error-log-prune:
    post:
      tags:
        - Cron
      summary: Create error log prune
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/integrations-refresh:
    post:
      tags:
        - Cron
      summary: Create integrations refresh
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/integrations-sync:
    post:
      tags:
        - Cron
      summary: Create integrations sync
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/orphan-projects-sweep:
    post:
      tags:
        - Cron
      summary: Create orphan projects sweep
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/recordings-reconcile:
    post:
      tags:
        - Cron
      summary: Create recordings reconcile
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/recordings-transcribe:
    post:
      tags:
        - Cron
      summary: Create recordings transcribe
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/recurring-tasks:
    post:
      tags:
        - Cron
      summary: Create recurring task
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/report-schedules:
    post:
      tags:
        - Cron
      summary: Create report schedule
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/scheduled-messages:
    post:
      tags:
        - Cron
      summary: Create scheduled message
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/sessions-cleanup:
    post:
      tags:
        - Cron
      summary: Create sessions cleanup
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/sla-breach-detector:
    post:
      tags:
        - Cron
      summary: Create sla breach detector
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/slip-risk-digest:
    post:
      tags:
        - Cron
      summary: Create slip risk digest
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/cron/stale-timers:
    post:
      tags:
        - Cron
      summary: Create stale timer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/custom-fields:
    get:
      tags:
        - Custom Fields
      summary: List custom fields
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Custom Fields
      summary: Create custom field
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/custom-fields/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Custom Fields
      summary: Delete custom field
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    put:
      tags:
        - Custom Fields
      summary: Update custom field
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/custom-statuses:
    get:
      tags:
        - Custom Statuses
      summary: List custom statuses
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Custom Statuses
      summary: Create a custom status
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/custom-statuses/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Custom Statuses
      summary: Delete a custom status
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    put:
      tags:
        - Custom Statuses
      summary: Update a custom status
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/status-required-fields:
    delete:
      tags:
        - Custom Statuses
      summary: Delete status required fields
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Custom Statuses
      summary: List status required fields
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Custom Statuses
      summary: Create status required field
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/dashboard/refresh:
    get:
      tags:
        - Dashboard
      summary: List refresh
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/data-governance/consent:
    get:
      tags:
        - Data Governance
      summary: List consent
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create consent
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/cookie-consent:
    get:
      tags:
        - Data Governance
      summary: List cookie consent
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create cookie consent
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/data-requests:
    get:
      tags:
        - Data Governance
      summary: List data requests
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create data request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/data-requests/{requestId}:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Data Governance
      summary: Update data request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/data-governance/dpa:
    get:
      tags:
        - Data Governance
      summary: List dpa
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create dpa
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/gdpr/delete:
    delete:
      tags:
        - Data Governance
      summary: Delete delete
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/data-governance/gdpr/export:
    get:
      tags:
        - Data Governance
      summary: List export
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/data-governance/workspace-export:
    get:
      tags:
        - Data Governance
      summary: Export the entire workspace as a JSON archive
      description: Admin/owner only. Reads across every workspace table, so it is rate limited to 5 requests per hour.
      parameters:
        - name: files
          in: query
          required: false
          description: How much file content to include.
          schema:
            type: string
            enum:
              - none
              - metadata
              - full
            default: full
      responses:
        "200":
          description: JSON archive
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          description: Unauthorized
        "403":
          description: Admin or owner required
        "404":
          description: No workspace found
        "429":
          description: Rate limited
  /api/data-governance/legal:
    get:
      tags:
        - Data Governance
      summary: List legal
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create legal
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/legal/accept:
    post:
      tags:
        - Data Governance
      summary: Create accept
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/pii:
    get:
      tags:
        - Data Governance
      summary: List pii
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create pii
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/data-governance/privacy-notice:
    get:
      tags:
        - Data Governance
      summary: List privacy notice
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Data Governance
      summary: Create privacy notice
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/trust/requests:
    post:
      tags:
        - Data Governance
      summary: Create request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/dev:
    get:
      tags:
        - Dev Integration
      summary: List dev
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/dev/repos:
    get:
      tags:
        - Dev Integration
      summary: List repos
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Dev Integration
      summary: Create repo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/angelsend/gmail/labels:
    get:
      tags:
        - Integrations
      summary: List labels
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/angelsend/gmail/messages:
    get:
      tags:
        - Integrations
      summary: List messages
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/angelsend/gmail/profile:
    get:
      tags:
        - Integrations
      summary: List profile
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/angelsend/gmail/send:
    post:
      tags:
        - Integrations
      summary: Create send
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/angelsend/gmail/status:
    get:
      tags:
        - Integrations
      summary: List status
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/angelsend/gmail/thread:
    get:
      tags:
        - Integrations
      summary: List thread
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/angelsend/oauth/callback:
    get:
      tags:
        - Integrations
      summary: List callback
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/angelsend/oauth/start:
    get:
      tags:
        - Integrations
      summary: List start
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/asana/tasks/{gid}:
    parameters:
      - name: gid
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Integrations
      summary: Delete task
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: Fetch task
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Integrations
      summary: Update task
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/asana/webhooks:
    post:
      tags:
        - Integrations
      summary: Create webhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/asana/webhooks/register:
    delete:
      tags:
        - Integrations
      summary: Delete register
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: List register
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Integrations
      summary: Create register
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/ai/chat:
    post:
      tags:
        - Integrations
      summary: Create chat
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/github/repos:
    get:
      tags:
        - Integrations
      summary: List repos
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/projects:
    get:
      tags:
        - Integrations
      summary: List projects
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Integrations
      summary: Create project
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/projects/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: Fetch project
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/projects/{id}/deployments:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: List deployments
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Integrations
      summary: Create deployment
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/projects/{id}/files:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: List files
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Integrations
      summary: Update files
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/cloudcode/projects/{id}/files/{path}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: path
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Integrations
      summary: Delete file
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: Fetch file
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/gmail/messages/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Integrations
      summary: Delete message
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: Fetch message
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Integrations
      summary: Update message
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/gmail/push:
    post:
      tags:
        - Integrations
      summary: Create push
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/gmail/watch:
    delete:
      tags:
        - Integrations
      summary: Delete watch
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Integrations
      summary: Create watch
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/google-calendar/events/{eventId}:
    parameters:
      - name: eventId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Integrations
      summary: Delete event
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: Fetch event
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Integrations
      summary: Update event
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/google-calendar/watch:
    post:
      tags:
        - Integrations
      summary: Create watch
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/google-calendar/watch/register:
    delete:
      tags:
        - Integrations
      summary: Delete register
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Integrations
      summary: Create register
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/google-drive/changes:
    post:
      tags:
        - Integrations
      summary: Create change
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/google-drive/files/{fileId}:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Integrations
      summary: Delete file
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: Fetch file
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Integrations
      summary: Update file
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/google-drive/watch:
    delete:
      tags:
        - Integrations
      summary: Delete watch
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Integrations
      summary: Create watch
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/agents:
    get:
      tags:
        - Integrations
      summary: List agents
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/agents/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: Fetch agent
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/agents/{id}/chat:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Integrations
      summary: Create chat
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/chat:
    post:
      tags:
        - Integrations
      summary: Create chat
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/models:
    get:
      tags:
        - Integrations
      summary: List models
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/workflows:
    get:
      tags:
        - Integrations
      summary: List workflows
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/workflows/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Integrations
      summary: Fetch workflow
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/multiverseai/workflows/{id}/execute:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Integrations
      summary: Create execute
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/scrappy/clients/{id}/push:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Integrations
      summary: Create push
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/scrappy/webhooks:
    post:
      tags:
        - Integrations
      summary: Create webhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/slack/events:
    post:
      tags:
        - Integrations
      summary: Create event
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/integrations/telegram/auth-test:
    get:
      tags:
        - Integrations
      summary: List auth test
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/telegram/link:
    delete:
      tags:
        - Integrations
      summary: Delete link
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Integrations
      summary: List link
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/integrations/telegram/webhook:
    post:
      tags:
        - Integrations
      summary: Create webhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/invites/{token}:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Invites
      summary: Fetch invite
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/invites/{token}/accept:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Invites
      summary: Create accept
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/invites/{token}/decline:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Invites
      summary: Create decline
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/invites/{token}/request-resend:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Invites
      summary: Create request resend
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/invites/mine:
    get:
      tags:
        - Invites
      summary: List mine
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/knowledge/ask:
    get:
      tags:
        - Knowledge
      summary: List ask
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Knowledge
      summary: Create ask
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/knowledge/autolink:
    get:
      tags:
        - Knowledge
      summary: List autolink
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Knowledge
      summary: Create autolink
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/knowledge/autolink/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Knowledge
      summary: Delete autolink
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Knowledge
      summary: Update autolink
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/knowledge/autolink/tokenize:
    post:
      tags:
        - Knowledge
      summary: Create tokenize
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/knowledge/index-stats:
    get:
      tags:
        - Knowledge
      summary: List index stats
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Knowledge
      summary: Create index stat
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/knowledge/readme:
    get:
      tags:
        - Knowledge
      summary: List readme
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Knowledge
      summary: Create readme
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/knowledge/decision-candidates:
    get:
      tags:
        - Knowledge
      summary: List AI-suggested decision candidates
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - accepted
              - dismissed
              - all
            default: pending
          description: Filter by review state.
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Knowledge
      summary: Scan documents for decision candidates (paginated; re-call with the returned offset until hasMore is false)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                offset:
                  type: integer
                  minimum: 0
                  description: Pagination offset from a prior call.
                limit:
                  type: integer
                  minimum: 1
                  maximum: 25
                  description: Documents to scan this call.
      responses:
        "200":
          description: "Scan result: scanned/found/hasMore/offset plus the pending candidates"
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "409":
          description: A scan is already running for this workspace
        "502":
          description: LLM error
        "503":
          description: No LLM configured
        "504":
          description: LLM timeout
  /api/knowledge/decision-candidates/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Knowledge
      summary: Accept or dismiss a decision candidate (terminal — accepting materializes a real decision record)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
              properties:
                action:
                  type: string
                  enum:
                    - accept
                    - dismiss
                decision:
                  type: object
                  description: Optional reviewer edits applied when accepting.
                  properties:
                    title:
                      type: string
                    context:
                      type: string
                    whatWeChose:
                      type: string
                    decidedAt:
                      type: string
                    status:
                      type: string
                    decidedBy:
                      type: string
      responses:
        "200":
          description: Success
        "400":
          description: Invalid action
        "401":
          description: Unauthorized
        "404":
          description: Candidate not found
        "409":
          description: Candidate was already reviewed
  /api/knowledge/stale:
    get:
      tags:
        - Knowledge
      summary: List stale
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Knowledge
      summary: Create stale
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/knowledge/stale/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Knowledge
      summary: Update stale
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/knowledge/threads/condensable:
    get:
      tags:
        - Knowledge
      summary: List condensable
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/knowledge/threads/condense:
    post:
      tags:
        - Knowledge
      summary: Create condense
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/legal-holds:
    get:
      tags:
        - Legal Holds
      summary: List legal holds
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Legal Holds
      summary: Create legal hold
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/legal-holds/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Legal Holds
      summary: Delete legal hold
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/stt:
    post:
      tags:
        - Media
      summary: Create stt
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tts:
    get:
      tags:
        - Media
      summary: List tts
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tts/speak:
    post:
      tags:
        - Media
      summary: Create speak
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/audit:
    get:
      tags:
        - Messages
      summary: List audit
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/audit/exports:
    get:
      tags:
        - Messages
      summary: List exports
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create export
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/audit/retention:
    get:
      tags:
        - Messages
      summary: List retention
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Messages
      summary: Update retention
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/channels:
    get:
      tags:
        - Messages
      summary: List channels
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create channel
      description: Creates a channel with the caller as its admin. Optional `memberIds` seeds initial members — ids are filtered to active members of the caller's workspace (ineligible ids are dropped, not an error).
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                description:
                  type: string
                isPrivate:
                  type: boolean
                projectRef:
                  type: string
                  nullable: true
                memberIds:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/channels/{channelId}:
    parameters:
      - name: channelId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Messages
      summary: Update channel
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/channels/{channelId}/members:
    parameters:
      - name: channelId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Messages
      summary: Leave a channel or remove a member
      description: With no body (or your own userId), leaves the channel — always allowed. With another user's id (JSON body `userId` or the `userId` query parameter), removes that member — workspace admins, channel admins, or the channel creator only.
      parameters:
        - name: userId
          in: query
          required: false
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
      responses:
        "200":
          description: Membership removed (idempotent)
        "403":
          description: Caller may not remove that member
        "404":
          description: Not found
    get:
      tags:
        - Messages
      summary: List members
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Join a channel or add a member
      description: With no body (or your own userId), joins the channel — public channels only; private channels require an invite. With another user's id (JSON body `userId`), adds that workspace member — the caller must be a channel member or a workspace admin, and this is the only way into a private channel. Adding an existing member is an idempotent no-op.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
      responses:
        "201":
          description: Member added / channel joined
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Caller may not add members / private channel requires invite
        "404":
          description: Not found
  /api/messages/client-channels:
    get:
      tags:
        - Messages
      summary: List client channels
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create client channel
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/client-channels/{channelId}/members:
    parameters:
      - name: channelId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Messages
      summary: Delete members
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Messages
      summary: List members
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create member
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/client-channels/{channelId}/messages:
    parameters:
      - name: channelId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Messages
      summary: List messages
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create message
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/files:
    get:
      tags:
        - Messages
      summary: List files
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/files/{messageId}/{attachmentId}:
    parameters:
      - name: messageId
        in: path
        required: true
        schema:
          type: string
      - name: attachmentId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Messages
      summary: Download a message attachment
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/mentions:
    get:
      tags:
        - Messages
      summary: List mentions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/notifications:
    get:
      tags:
        - Messages
      summary: List notifications
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Messages
      summary: Update notifications
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/presence:
    post:
      tags:
        - Messages
      summary: Create presence
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/project-links:
    delete:
      tags:
        - Messages
      summary: Delete project links
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Messages
      summary: List project links
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create project link
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/push/subscribe:
    delete:
      tags:
        - Messages
      summary: Delete subscribe
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Messages
      summary: Create subscribe
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/push/vapid-key:
    get:
      tags:
        - Messages
      summary: List vapid key
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/scheduled:
    get:
      tags:
        - Messages
      summary: List scheduled
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create scheduled
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/scheduled-processor:
    post:
      tags:
        - Messages
      summary: Create scheduled processor
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/scheduled/{messageId}:
    parameters:
      - name: messageId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Messages
      summary: Delete scheduled
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Messages
      summary: Update scheduled
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/search:
    get:
      tags:
        - Messages
      summary: List search
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/stream:
    get:
      tags:
        - Messages
      summary: List stream
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/templates:
    get:
      tags:
        - Messages
      summary: List templates
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create template
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/templates/{templateId}:
    parameters:
      - name: templateId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Messages
      summary: Delete template
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Messages
      summary: Fetch template
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Messages
      summary: Update template
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Action on template
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/meeting/recording:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Messages
      summary: List recording
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create recording
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/meeting/recording/{recordingId}:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: recordingId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Messages
      summary: Delete recording
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Messages
      summary: Fetch recording
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/meeting/recording/{recordingId}/chunk:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: recordingId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Messages
      summary: Create chunk
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/meeting/recording/{recordingId}/consent:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: recordingId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Messages
      summary: Create consent
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/meeting/recording/{recordingId}/stop:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: recordingId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Messages
      summary: Create stop
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/meeting/recording/{recordingId}/transcript:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: recordingId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Messages
      summary: List transcript
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Create transcript
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/messages/{messageId}/read:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: messageId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Messages
      summary: Create read
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/messages/{messageId}/replies:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
      - name: messageId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Messages
      summary: List replies
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Messages
      summary: Reply in a message thread
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/messages/forward:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Messages
      summary: Create forward
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/messages/threads/{contactId}/pinned:
    parameters:
      - name: contactId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Messages
      summary: List pinned
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/notifications/admin/escalation-rules:
    delete:
      tags:
        - Notifications
      summary: Delete escalation rules
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List escalation rules
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update escalation rules
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create escalation rule
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/admin/health-alerts:
    delete:
      tags:
        - Notifications
      summary: Delete health alerts
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List health alerts
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update health alerts
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create health alert
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/admin/metrics:
    get:
      tags:
        - Notifications
      summary: List metrics
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/notifications/admin/rules:
    delete:
      tags:
        - Notifications
      summary: Delete rules
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List rules
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update rules
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create rule
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/admin/teams:
    delete:
      tags:
        - Notifications
      summary: Delete teams
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List teams
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create team
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/admin/workspace-defaults:
    get:
      tags:
        - Notifications
      summary: List workspace defaults
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update workspace defaults
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/notifications/digest:
    get:
      tags:
        - Notifications
      summary: List digest
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update digest
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/notifications/dispatch:
    post:
      tags:
        - Notifications
      summary: Create dispatch
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/email-event:
    post:
      tags:
        - Notifications
      summary: Create email event
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/muted-threads:
    delete:
      tags:
        - Notifications
      summary: Delete muted threads
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List muted threads
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create muted thread
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/preferences:
    get:
      tags:
        - Notifications
      summary: List preferences
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update preferences
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/notifications/process:
    post:
      tags:
        - Notifications
      summary: Process pending notifications
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/push-tokens:
    delete:
      tags:
        - Notifications
      summary: Delete push tokens
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List push tokens
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create push token
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/quiet-hours:
    get:
      tags:
        - Notifications
      summary: List quiet hours
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Notifications
      summary: Update quiet hours
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/notifications/subscriptions:
    delete:
      tags:
        - Notifications
      summary: Delete subscriptions
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Notifications
      summary: List subscriptions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create subscription
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/notifications/unsubscribe:
    get:
      tags:
        - Notifications
      summary: List unsubscribe
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Notifications
      summary: Create unsubscribe
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/onboarding/config:
    get:
      tags:
        - Onboarding
      summary: List config
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Onboarding
      summary: Update config
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/onboarding/progress:
    get:
      tags:
        - Onboarding
      summary: List progress
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Onboarding
      summary: Update progress
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/budget-burn:
    get:
      tags:
        - Planning
      summary: List budget burn
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/calibration:
    get:
      tags:
        - Planning
      summary: List calibration
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/critical-path:
    get:
      tags:
        - Planning
      summary: List critical path
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/dependencies:
    get:
      tags:
        - Planning
      summary: List dependencies
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/grooming:
    get:
      tags:
        - Planning
      summary: List grooming
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/grooming/apply:
    post:
      tags:
        - Planning
      summary: Create apply
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/planning/leveling:
    get:
      tags:
        - Planning
      summary: List leveling
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/leveling/apply:
    post:
      tags:
        - Planning
      summary: Create apply
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/planning/scenarios:
    get:
      tags:
        - Planning
      summary: List scenarios
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/scope-creep:
    get:
      tags:
        - Planning
      summary: List scope creep
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/scope-creep/baseline:
    post:
      tags:
        - Planning
      summary: Create baseline
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/planning/sprint-planner:
    get:
      tags:
        - Planning
      summary: List sprint planner
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Planning
      summary: Create sprint planner
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/planning/viable-scope:
    get:
      tags:
        - Planning
      summary: List viable scope
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/planning/viable-scope/apply:
    post:
      tags:
        - Planning
      summary: Create apply
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio:
    get:
      tags:
        - Portfolio
      summary: List portfolio
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/portfolio/budgets:
    get:
      tags:
        - Portfolio
      summary: List budgets
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create budget
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio/budgets/entries:
    get:
      tags:
        - Portfolio
      summary: List entries
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create a budget entry
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio/forecasts:
    get:
      tags:
        - Portfolio
      summary: List forecasts
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/portfolio/objectives:
    delete:
      tags:
        - Portfolio
      summary: Delete objectives
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Portfolio
      summary: List objectives
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Portfolio
      summary: Update objectives
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create objective
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio/programs:
    delete:
      tags:
        - Portfolio
      summary: Delete programs
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Portfolio
      summary: List programs
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Portfolio
      summary: Update programs
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create program
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio/resources/allocations:
    delete:
      tags:
        - Portfolio
      summary: Delete allocations
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Portfolio
      summary: List allocations
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Portfolio
      summary: Update allocations
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create allocation
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio/resources/capacity:
    get:
      tags:
        - Portfolio
      summary: List capacity
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create capacity
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/portfolio/resources/requests:
    get:
      tags:
        - Portfolio
      summary: List requests
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Portfolio
      summary: Update requests
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Portfolio
      summary: Create request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/project-keys:
    get:
      tags:
        - Projects
      summary: List project keys
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Projects
      summary: Create project key
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
    put:
      tags:
        - Projects
      summary: Update project keys
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/project-templates:
    get:
      tags:
        - Projects
      summary: List project templates
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Projects
      summary: Create project template
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/project-templates/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Projects
      summary: Delete project template
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Projects
      summary: Fetch project template
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Projects
      summary: Update project template
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/project-templates/{id}/instantiate:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Projects
      summary: Create instantiate
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/{id}/icon:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Projects
      summary: Delete icon
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Projects
      summary: Create icon
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/{id}/members:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Projects
      summary: Add a member to a project
      description: Admin/owner only. Identify the person by `userId` or by `email`. The user must be an active member of THIS workspace; an email that is not yet a member raises a pending workspace invite as part of the same call, redeemable in-app via /api/invites/mine. Re-adding someone is a no-op and returns 200.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
                  description: Must be an active member of this workspace.
                email:
                  type: string
                  description: Alternative to userId. Invited if not yet a member.
                team:
                  type: string
      responses:
        "200":
          description: Already a member — unchanged
        "201":
          description: Member added
        "400":
          description: Neither userId nor email supplied
        "401":
          description: Unauthorized
        "404":
          description: Project not found
          or user not in this workspace: null
    delete:
      tags:
        - Projects
      summary: Remove a member from a project
      description: Admin/owner only. Identify by `email` (the ACL key) or `userId`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                userId:
                  type: string
      responses:
        "200":
          description: Member removed (or was not present)
        "400":
          description: Neither userId nor email supplied
        "404":
          description: Project not found
  /api/projects/{id}/members/{userId}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: userId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Projects
      summary: Delete member
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/projects/{id}/teams:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Projects
      summary: Delete teams
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Projects
      summary: Create team
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/{id}/visibility:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Projects
      summary: Update visibility
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/projects/archive:
    post:
      tags:
        - Projects
      summary: Create archive
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/budget:
    get:
      tags:
        - Projects
      summary: List budget
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Projects
      summary: Create budget
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/budget/entries:
    delete:
      tags:
        - Projects
      summary: Delete entries
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Projects
      summary: Create a budget entry
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/duplicate:
    post:
      tags:
        - Projects
      summary: Create duplicate
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/health:
    get:
      tags:
        - Projects
      summary: List health
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Projects
      summary: Update health
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/projects/phases:
    get:
      tags:
        - Projects
      summary: List phases
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Projects
      summary: Create phase
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/projects/phases/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Projects
      summary: Delete phase
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Projects
      summary: Update phase
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/projects/roadmap:
    get:
      tags:
        - Projects
      summary: List roadmap
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/access-audit:
    get:
      tags:
        - RBAC
      summary: List access audit
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/audit:
    get:
      tags:
        - RBAC
      summary: List audit
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/default-permissions:
    get:
      tags:
        - RBAC
      summary: List default permissions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update default permissions
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/departments:
    get:
      tags:
        - RBAC
      summary: List departments
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - RBAC
      summary: Create department
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/rbac/departments/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - RBAC
      summary: Delete department
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - RBAC
      summary: Update department
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/departments/{id}/members:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - RBAC
      summary: Delete members
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - RBAC
      summary: List members
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - RBAC
      summary: Create member
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/rbac/explorer:
    get:
      tags:
        - RBAC
      summary: List explorer
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/field-permissions:
    delete:
      tags:
        - RBAC
      summary: Delete field permissions
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - RBAC
      summary: List field permissions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update field permissions
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/project-overrides:
    delete:
      tags:
        - RBAC
      summary: Delete project overrides
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - RBAC
      summary: List project overrides
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update project overrides
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/project-visibility:
    delete:
      tags:
        - RBAC
      summary: Delete project visibility
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - RBAC
      summary: List project visibility
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update project visibility
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/role-change-requests:
    get:
      tags:
        - RBAC
      summary: List role change requests
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - RBAC
      summary: Create role change request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/rbac/role-change-requests/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - RBAC
      summary: Update role change request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/roles:
    get:
      tags:
        - RBAC
      summary: List roles
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - RBAC
      summary: Create role
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/rbac/roles/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - RBAC
      summary: Delete role
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - RBAC
      summary: Fetch role
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - RBAC
      summary: Update role
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/roles/{id}/duplicate:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - RBAC
      summary: Create duplicate
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/rbac/roles/{id}/preview:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - RBAC
      summary: List preview
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/share-links:
    get:
      tags:
        - RBAC
      summary: List share links
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/share-links/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - RBAC
      summary: Delete share link
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - RBAC
      summary: Update share link
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/sharing-controls:
    get:
      tags:
        - RBAC
      summary: List sharing controls
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update sharing controls
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/team-permissions:
    delete:
      tags:
        - RBAC
      summary: Delete team permissions
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - RBAC
      summary: List team permissions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update team permissions
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/rbac/time-bound-grants:
    get:
      tags:
        - RBAC
      summary: List time bound grants
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - RBAC
      summary: Create time bound grant
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/rbac/time-bound-grants/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - RBAC
      summary: Delete time bound grant
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/rbac/user-roles:
    get:
      tags:
        - RBAC
      summary: List user roles
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - RBAC
      summary: Update user roles
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports:
    get:
      tags:
        - Reports
      summary: List reports
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Reports
      summary: Create report
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Reports
      summary: Delete report
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Reports
      summary: Fetch report
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports/dashboards:
    get:
      tags:
        - Reports
      summary: List dashboards
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Reports
      summary: Create dashboard
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/dashboards/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Reports
      summary: Delete dashboard
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Reports
      summary: Fetch dashboard
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Reports
      summary: Update dashboard
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports/dashboards/{id}/widgets:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Reports
      summary: List widgets
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Reports
      summary: Create widget
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/dashboards/{id}/widgets/{widgetId}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: widgetId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Reports
      summary: Delete widget
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Reports
      summary: Update widget
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports/embed/{token}:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Reports
      summary: Fetch embed
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports/export:
    post:
      tags:
        - Reports
      summary: Create export
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/operating:
    get:
      tags:
        - Reports
      summary: Operating metrics report
      parameters:
        - name: days
          in: query
          required: false
          description: Lookback window. Non-numeric or non-positive values fall back to 90.
          schema:
            type: integer
            default: 90
            minimum: 1
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports/filters:
    get:
      tags:
        - Reports
      summary: List filters
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Reports
      summary: Create filter
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/filters/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Reports
      summary: Delete filter
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Reports
      summary: Fetch filter
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/reports/generate:
    post:
      tags:
        - Reports
      summary: Create generate
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/insights:
    post:
      tags:
        - Reports
      summary: Create insight
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/insights/narrative:
    post:
      tags:
        - Reports
      summary: Create narrative
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/state-of-business:
    get:
      tags:
        - Reports
      summary: Get the State of the Business report config + status
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Reports
      summary: Configure the State of the Business report (admin)
      description: Create/update the report + schedule and arm the opt-in flag. Admin only.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
  /api/reports/schedules:
    get:
      tags:
        - Reports
      summary: List schedules
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Reports
      summary: Create schedule
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/reports/schedules/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Reports
      summary: Delete schedule
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Reports
      summary: Update schedule
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/resolutions:
    get:
      tags:
        - Resolutions
      summary: List resolutions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Resolutions
      summary: Create resolution
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/resolutions/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Resolutions
      summary: Delete resolution
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    put:
      tags:
        - Resolutions
      summary: Update resolution
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/scim/v2/Groups:
    get:
      tags:
        - SCIM
      summary: List Groups
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - SCIM
      summary: Create Group
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/scim/v2/Groups/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - SCIM
      summary: Delete Group
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - SCIM
      summary: Fetch Group
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - SCIM
      summary: Update Group
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - SCIM
      summary: Update Group
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/scim/v2/ResourceTypes:
    get:
      tags:
        - SCIM
      summary: List ResourceTypes
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/scim/v2/Schemas:
    get:
      tags:
        - SCIM
      summary: List Schemas
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/scim/v2/ServiceProviderConfig:
    get:
      tags:
        - SCIM
      summary: List ServiceProviderConfig
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/scim/v2/Users:
    get:
      tags:
        - SCIM
      summary: List Users
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - SCIM
      summary: Create User
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/scim/v2/Users/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - SCIM
      summary: Delete User
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - SCIM
      summary: Fetch User
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - SCIM
      summary: Update User
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - SCIM
      summary: Update User
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/balance:
    get:
      tags:
        - Settings
      summary: List balance
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/cancel:
    post:
      tags:
        - Settings
      summary: Create cancel
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/settings/billing/invoices:
    get:
      tags:
        - Settings
      summary: List invoices
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/invoices/{id}/pdf:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Settings
      summary: List pdf
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/order:
    post:
      tags:
        - Settings
      summary: Create order
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/settings/billing/payment-method:
    get:
      tags:
        - Settings
      summary: List payment method
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Settings
      summary: Create payment method
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/settings/billing/profile:
    get:
      tags:
        - Settings
      summary: List profile
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Settings
      summary: Update profile
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/tiers:
    get:
      tags:
        - Settings
      summary: List tiers
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/trial:
    get:
      tags:
        - Settings
      summary: List trial
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/billing/webhook:
    post:
      tags:
        - Settings
      summary: Create webhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/settings/privacy/data-requests/{requestId}/download:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Settings
      summary: Download a completed data-export request
      description: Returns the stored export as a JSON attachment. Only valid for requests of type export that have completed.
      responses:
        "200":
          description: JSON export file
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Request is not a data export
        "401":
          description: Unauthorized
        "404":
          description: Request not found or no workspace
        "409":
          description: Export not ready
        "500":
          description: Stored export could not be read
  /api/settings/privacy:
    get:
      tags:
        - Settings
      summary: List privacy
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Settings
      summary: Create privacy
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/settings/sessions:
    get:
      tags:
        - Settings
      summary: List sessions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/settings/sessions/{sessionId}:
    parameters:
      - name: sessionId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Settings
      summary: Delete session
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/sprints:
    get:
      tags:
        - Sprints
      summary: List sprints
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Sprints
      summary: Create sprint
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/sprints/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Sprints
      summary: Delete sprint
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Sprints
      summary: Fetch sprint
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Sprints
      summary: Update sprint
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/contact:
    post:
      tags:
        - Support
      summary: Create contact
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/app-config/public:
    get:
      tags:
        - System
      summary: List public
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/company-logo:
    get:
      tags:
        - System
      summary: List company logo
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/company-preview:
    get:
      tags:
        - System
      summary: List company preview
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/system/client-error:
    post:
      tags:
        - System
      summary: Create client error
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/system/cron-health:
    get:
      tags:
        - System
      summary: List cron health
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/system/health:
    get:
      tags:
        - System
      summary: List health
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
        "503":
          description: A sub-check failed — database
          SMTP: null
          storage: null
          a stale cron: null
          or native push (APNs unconfigured/unsignable while iOS or macOS device tokens are registered): null
  /api/system/realtime-health:
    get:
      tags:
        - System
      summary: List realtime health
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
        "503":
          description: Realtime stack degraded — VAPID or TURN not configured
          the push census queries failed (DB unreachable / migration not applied): null
          or APNs is unconfigured while native device tokens are registered: null
  /api/system/web-vitals:
    post:
      tags:
        - System
      summary: Create web vital
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/undo:
    get:
      tags:
        - System
      summary: List undo
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - System
      summary: Create undo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/undo/redo:
    post:
      tags:
        - System
      summary: Create redo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/uploads/{path}:
    parameters:
      - name: path
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - System
      summary: Fetch upload
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/task-types:
    get:
      tags:
        - Task Types
      summary: List task types
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Task Types
      summary: Create task type
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/task-types/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Task Types
      summary: Delete task type
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    put:
      tags:
        - Task Types
      summary: Update task type
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/{id}/attachments:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Tasks
      summary: Delete attachments
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Tasks
      summary: List attachments
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create attachment
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/{id}/comments:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: integer
    get:
      tags:
        - Tasks
      summary: List a task's comments
      responses:
        "200":
          description: Comments
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  comments:
                    type: array
                    items:
                      $ref: "#/components/schemas/TaskComment"
        "404":
          description: Task not found or not visible to the caller
    post:
      tags:
        - Tasks
      summary: Append a comment to a task
      description: Appends a single comment atomically. Prefer this over PATCHing the task's whole `comments` array, which is a read-modify-write and loses concurrent comments.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                content:
                  type: string
                  description: Alias for `body`.
      responses:
        "201":
          description: Comment appended
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  comment:
                    $ref: "#/components/schemas/TaskComment"
                  comments:
                    type: array
                    items:
                      $ref: "#/components/schemas/TaskComment"
        "400":
          description: Missing comment body
        "404":
          description: Task not found or not visible to the caller
  /api/tasks/{id}/checklist:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Tasks
      summary: List checklist
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Tasks
      summary: Update checklist
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/{id}/estimate:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Tasks
      summary: List estimate
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create estimate
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/{id}/suggest-split:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Tasks
      summary: Create suggest split
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/{id}/why-stuck:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Tasks
      summary: Create why stuck
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/activity:
    get:
      tags:
        - Tasks
      summary: List activity
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/approvals:
    get:
      tags:
        - Tasks
      summary: List approvals
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create approval
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/approvals/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Tasks
      summary: Update approval
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/batch:
    get:
      tags:
        - Tasks
      summary: List batch
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/bulk:
    patch:
      tags:
        - Tasks
      summary: Update bulk
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/dependencies:
    get:
      tags:
        - Tasks
      summary: List dependencies
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create a task dependency
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/dependencies/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Tasks
      summary: Delete a task dependency
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/tasks/effort:
    patch:
      tags:
        - Tasks
      summary: Update effort
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/gantt:
    get:
      tags:
        - Tasks
      summary: List gantt
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/intelligence/decompose:
    post:
      tags:
        - Tasks
      summary: Create decompose
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/entity-links:
    post:
      tags:
        - Tasks
      summary: Create entity link
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/eta-confidence:
    post:
      tags:
        - Tasks
      summary: Create eta confidence
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/find-duplicates:
    post:
      tags:
        - Tasks
      summary: Create find duplicate
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/parse:
    post:
      tags:
        - Tasks
      summary: Create parse
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/skills:
    post:
      tags:
        - Tasks
      summary: Create skill
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/slip-risk:
    post:
      tags:
        - Tasks
      summary: Create slip risk
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/intelligence/templates:
    get:
      tags:
        - Tasks
      summary: List templates
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create template
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/mentions:
    get:
      tags:
        - Tasks
      summary: List mentions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Tasks
      summary: Update mentions
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create mention
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/milestones:
    get:
      tags:
        - Tasks
      summary: List milestones
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create milestone
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/milestones/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Tasks
      summary: Delete milestone
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Tasks
      summary: Update milestone
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/tasks/subscriptions:
    delete:
      tags:
        - Tasks
      summary: Delete subscriptions
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    get:
      tags:
        - Tasks
      summary: List subscriptions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Tasks
      summary: Create subscription
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/tasks/views:
    get:
      tags:
        - Tasks
      summary: List views
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/time-productivity/capture:
    get:
      tags:
        - Time Productivity
      summary: List capture
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Time Productivity
      summary: Create capture
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/time-productivity/focus:
    get:
      tags:
        - Time Productivity
      summary: Get focus session state
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Time Productivity
      summary: Start a focus session
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/time-productivity/insights:
    get:
      tags:
        - Time Productivity
      summary: List insights
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/time-productivity/plan:
    get:
      tags:
        - Time Productivity
      summary: List plan
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Time Productivity
      summary: Update plan
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Time Productivity
      summary: Create plan
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/versions:
    get:
      tags:
        - Versions
      summary: List versions
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Versions
      summary: Create version
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/versions/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Versions
      summary: Delete version
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    put:
      tags:
        - Versions
      summary: Update version
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace:
    delete:
      tags:
        - Workspace
      summary: Delete workspace
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
  /api/workspace/create:
    post:
      tags:
        - Workspace
      summary: Create a new workspace
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/guests:
    get:
      tags:
        - Workspace
      summary: List guests
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Workspace
      summary: Create guest
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/guests/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Workspace
      summary: Delete guest
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Workspace
      summary: Update guest
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace/invites:
    get:
      tags:
        - Workspace
      summary: List invites
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Workspace
      summary: Create invite
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/invites/{inviteId}:
    parameters:
      - name: inviteId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Workspace
      summary: Delete invite
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Workspace
      summary: Update invite
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace/invites/{inviteId}/resend:
    parameters:
      - name: inviteId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Workspace
      summary: Create resend
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/invites/{inviteId}/approve:
    parameters:
      - name: inviteId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Workspace
      summary: Approve a member-raised invite request
      description: |
        Admin-only. Approves an invite with status `requested` (created by a member when the workspace `invite_policy` is `members-with-approval`), rotating its token and expiry and sending the invite email. Returns 409 if the invite is not in the `requested` state.
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Not found
        "409":
          description: Invite is not awaiting approval
  /api/workspace/invites/bulk:
    post:
      tags:
        - Workspace
      summary: Create bulk
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/invites/csv:
    post:
      tags:
        - Workspace
      summary: Create csv
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/list:
    get:
      tags:
        - Workspace
      summary: List the caller's workspaces
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace/members:
    get:
      tags:
        - Workspace
      summary: List members
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Workspace
      summary: Create member
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/members/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Workspace
      summary: Delete member
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    patch:
      tags:
        - Workspace
      summary: Update member
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace/members/{userId}/deprovision:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Workspace
      summary: List deprovision
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Workspace
      summary: Create deprovision
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/members/{userId}/transfer:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Workspace
      summary: List transfer
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    post:
      tags:
        - Workspace
      summary: Create transfer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/recording-policy:
    get:
      tags:
        - Workspace
      summary: List recording policy
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    patch:
      tags:
        - Workspace
      summary: Update recording policy
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace/seats:
    get:
      tags:
        - Workspace
      summary: List seats
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
    put:
      tags:
        - Workspace
      summary: Update seats
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
  /api/workspace/seats/named/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema:
          type: string
    delete:
      tags:
        - Workspace
      summary: Remove a named seat from a member
      responses:
        "200":
          description: Deleted
        "404":
          description: Not found
    post:
      tags:
        - Workspace
      summary: Assign a named seat to a member
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/workspace/switch:
    post:
      tags:
        - Workspace
      summary: Switch the active workspace (session callers only)
      description: |
        Sets the browser's active-workspace cookie after validating membership. API-key callers get 409 — keys are bound to one workspace at mint time and cannot switch; mint a key in the target workspace instead.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - workspaceId
              properties:
                workspaceId:
                  type: string
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Not a member of that workspace
        "409":
          description: Caller authenticated with a workspace-bound API key
  /api/ediscovery/export:
    post:
      tags:
        - eDiscovery
      summary: Create export
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/ediscovery/search:
    post:
      tags:
        - eDiscovery
      summary: Create search
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Success
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/github/app/install:
    get:
      tags:
        - Integrations
      summary: Start the GitHub App install flow (302 redirects to github.com)
      responses:
        "302":
          description: Redirect to GitHub install URL
        "401":
          description: Unauthorized
  /api/github/app/callback:
    get:
      tags:
        - Integrations
      summary: GitHub App install callback (handles installation_id, redirects back to app)
      parameters:
        - name: installation_id
          in: query
          schema:
            type: string
        - name: setup_action
          in: query
          schema:
            type: string
        - name: state
          in: query
          schema:
            type: string
      responses:
        "302":
          description: Redirect back to the integrations UI
        "401":
          description: Unauthorized
  /api/github/oauth/start:
    get:
      tags:
        - Integrations
      summary: Start the GitHub OAuth sign-in flow (302 redirects to github.com)
      responses:
        "302":
          description: Redirect to GitHub authorize URL
        "401":
          description: Unauthorized
  /api/github/repos:
    get:
      tags:
        - Integrations
      summary: List GitHub repositories available to connect
      responses:
        "200":
          description: Repository list
        "401":
          description: Unauthorized
        "412":
          description: GitHub not connected
  /api/github/status:
    get:
      tags:
        - Integrations
      summary: Get the workspace GitHub connection status
      responses:
        "200":
          description: Connection status
        "401":
          description: Unauthorized
  /api/github/sync:
    post:
      tags:
        - Integrations
      summary: Trigger a GitHub sync for connected repositories
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                repoId:
                  type: string
      responses:
        "200":
          description: Sync started or completed
        "401":
          description: Unauthorized
        "412":
          description: GitHub not connected
  /api/github/webhook:
    post:
      tags:
        - Integrations
      summary: Receive GitHub webhook deliveries (signature-verified, unauthenticated)
      description: Inbound GitHub webhook. Verified via the `X-Hub-Signature-256` header rather than a session.
      responses:
        "200":
          description: Event accepted
        "202":
          description: Event ignored
        "401":
          description: Invalid signature
  /api/requests:
    get:
      tags:
        - Requests
      summary: List intake requests for the workspace (members see their own; admins see all)
      responses:
        "200":
          description: Requests list
        "400":
          description: No active workspace
    patch:
      tags:
        - Requests
      summary: Update the status of an intake request
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
                - status
              properties:
                id:
                  type: string
                status:
                  type: string
      responses:
        "200":
          description: Request updated
        "400":
          description: Missing id or invalid status
        "403":
          description: Forbidden
        "404":
          description: Not found
    post:
      tags:
        - Requests
      summary: Convert an intake request into a task
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
                - projectRef
              properties:
                id:
                  type: string
                projectRef:
                  type: string
                assignedTo:
                  type: string
                  nullable: true
      responses:
        "200":
          description: Task created from request
        "400":
          description: Missing id or projectRef
        "403":
          description: Forbidden
        "404":
          description: Not found
        "409":
          description: Request already converted
  /api/requests/count:
    get:
      tags:
        - Requests
      summary: Get the count of open intake requests (for nav badges)
      responses:
        "200":
          description: Request count
  /api/requests/pages:
    get:
      tags:
        - Requests
      summary: List request intake links (pages)
      responses:
        "200":
          description: Request pages
    post:
      tags:
        - Requests
      summary: Create a request intake link
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
              properties:
                title:
                  type: string
                description:
                  type: string
                accessMode:
                  type: string
                  enum:
                    - public
                    - clients_only
                scope:
                  type: string
                  enum:
                    - user
                    - workspace
      responses:
        "200":
          description: Page created
        "400":
          description: Invalid body / title
        "403":
          description: Only admins can create workspace-level links
    patch:
      tags:
        - Requests
      summary: Update a request intake link
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
                title:
                  type: string
                description:
                  type: string
                  nullable: true
                accessMode:
                  type: string
                  enum:
                    - public
                    - clients_only
                isActive:
                  type: boolean
      responses:
        "200":
          description: Page updated
        "400":
          description: Missing id / invalid field
        "403":
          description: Forbidden
        "404":
          description: Not found
    delete:
      tags:
        - Requests
      summary: Delete a request intake link
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Page deleted
        "400":
          description: Missing id
        "403":
          description: Forbidden
        "404":
          description: Not found
  /api/requests/pages/{id}/fields:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Requests
      summary: List the custom fields for a request intake link
      responses:
        "200":
          description: Fields list
        "404":
          description: Not found
    put:
      tags:
        - Requests
      summary: Replace the custom fields for a request intake link
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - fields
              properties:
                fields:
                  type: array
                  items:
                    type: object
      responses:
        "200":
          description: Fields replaced
        "400":
          description: Invalid fields
        "403":
          description: Forbidden
        "404":
          description: Not found
  /api/r/verify:
    get:
      tags:
        - Requests
      summary: Confirm a public request submission via emailed token (returns an HTML page)
      description: Public, unauthenticated. Consumes a one-time verification token and finalizes the submission.
      parameters:
        - name: token
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: HTML confirmation page (success
          expired: null
          or invalid): null
  /api/r/{token}/submit:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Requests
      summary: Submit a public intake request against a link token
      description: Public, unauthenticated (rate limited). Accepts JSON or multipart form data with `subject`, `body`, requester identity, and any custom fields. Links in `clients_only` mode require a portal session.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - subject
                - body
              properties:
                subject:
                  type: string
                body:
                  type: string
                name:
                  type: string
                email:
                  type: string
          multipart/form-data:
            schema:
              type: object
      responses:
        "200":
          description: Submission accepted (may require email verification)
        "400":
          description: Invalid submission
        "404":
          description: Link no longer available
        "429":
          description: Rate limited
  /api/people/skills:
    get:
      tags:
        - People
      summary: List declared/learned skill profiles for workspace members
      parameters:
        - name: member
          in: query
          schema:
            type: string
          description: Filter to a single member by display name.
      responses:
        "200":
          description: Skill profiles
        "401":
          description: Unauthorized
    put:
      tags:
        - People
      summary: Replace a member's declared skill set
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - memberName
                - skills
              properties:
                memberName:
                  type: string
                skills:
                  type: array
                  items:
                    type: object
                    required:
                      - skill
                      - strength
                    properties:
                      skill:
                        type: string
                        description: A canonical vocabulary key, or the member's own custom free-text skill.
                      strength:
                        type: number
                        minimum: 0
                        maximum: 1
                        description: Declared baseline strength; reinforcement may raise the stored value later.
      responses:
        "200":
          description: Skills updated
        "400":
          description: Invalid body
        "401":
          description: Unauthorized
  /api/portfolio/availability/requests:
    get:
      tags:
        - Portfolio
      summary: List availability requests (leader-to-team asks)
      responses:
        "200":
          description: Availability requests
        "400":
          description: No active workspace
    post:
      tags:
        - Portfolio
      summary: Create an availability request (admin only)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
                - periodStart
                - periodEnd
              properties:
                projectRef:
                  type: string
                  nullable: true
                  description: null = workspace-wide
                title:
                  type: string
                message:
                  type: string
                periodStart:
                  type: string
                  format: date
                periodEnd:
                  type: string
                  format: date
                dueDate:
                  type: string
                  format: date
                  nullable: true
      responses:
        "200":
          description: Request created
        "400":
          description: Invalid body
        "403":
          description: Admin only
    patch:
      tags:
        - Portfolio
      summary: Update an availability request status (admin only)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
                status:
                  type: string
                  enum:
                    - open
                    - closed
      responses:
        "200":
          description: Request updated
        "400":
          description: Invalid body
        "403":
          description: Admin only
        "404":
          description: Not found
    delete:
      tags:
        - Portfolio
      summary: Delete an availability request (admin only)
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Request deleted
        "400":
          description: Missing id
        "403":
          description: Admin only
        "404":
          description: Not found
  /api/portfolio/availability/submissions:
    get:
      tags:
        - Portfolio
      summary: List availability submissions (admins see all; members see their own)
      parameters:
        - name: requestId
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Availability submissions
        "400":
          description: No active workspace
    post:
      tags:
        - Portfolio
      summary: Upsert the caller's own availability submission
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                requestId:
                  type: string
                  nullable: true
                  description: Omit for a standing entry
                status:
                  type: string
                  enum:
                    - available
                    - limited
                    - unavailable
                availableHoursPerWeek:
                  type: number
                periodStart:
                  type: string
                  format: date
                  nullable: true
                periodEnd:
                  type: string
                  format: date
                  nullable: true
                timeOff:
                  type: array
                  items:
                    type: object
                    properties:
                      start:
                        type: string
                      end:
                        type: string
                      note:
                        type: string
                notes:
                  type: string
      responses:
        "200":
          description: Submission saved
        "400":
          description: Invalid body
  /api/ai-agent/personas:
    get:
      tags:
        - AI Agent
      summary: List AI agent personas (AI employees)
      responses:
        "200":
          description: Personas
    post:
      tags:
        - AI Agent
      summary: Create an AI agent persona
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                roleTitle:
                  type: string
                model:
                  type: string
                systemPrompt:
                  type: string
                avatar:
                  type: string
      responses:
        "201":
          description: Persona created
        "400":
          description: Invalid input
        "403":
          description: AI agent disabled
  /api/ai-agent/personas/generate:
    post:
      tags:
        - AI Agent
      summary: Draft a persona from a natural-language description
      description: Admin/owner only. Returns a generated (unsaved) persona.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - prompt
              properties:
                prompt:
                  type: string
                  maxLength: 2000
                  description: Description of the AI employee. Truncated at 2000 chars.
      responses:
        "200":
          description: Persona draft generated
        "400":
          description: Prompt missing or empty
        "401":
          description: Unauthorized
        "403":
          description: AI agent disabled
          or admin/owner required: null
  /api/ai-agent/personas/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - AI Agent
      summary: Update an AI agent persona
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                roleTitle:
                  type: string
                model:
                  type: string
                systemPrompt:
                  type: string
                avatar:
                  type: string
      responses:
        "200":
          description: Persona updated
        "400":
          description: Invalid input
        "403":
          description: AI agent disabled
        "404":
          description: Not found
    delete:
      tags:
        - AI Agent
      summary: Delete an AI agent persona
      responses:
        "200":
          description: Persona deleted
        "403":
          description: AI agent disabled
        "404":
          description: Not found
  /api/me/notes:
    get:
      tags:
        - Personal
      summary: Get the caller's scratch note
      responses:
        "200":
          description: The note (may be null if never set)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  note:
                    nullable: true
    put:
      tags:
        - Personal
      summary: Upsert the caller's scratch note
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - body
              properties:
                body:
                  type: string
                  description: Note body (capped, 413 if too large).
      responses:
        "200":
          description: Note saved
        "400":
          description: Invalid body
        "413":
          description: Note exceeds the size cap
  /api/me/reminders:
    get:
      tags:
        - Personal
      summary: List the caller's reminders
      responses:
        "200":
          description: Reminders
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  reminders:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Personal
      summary: Create a reminder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - text
                - remindAt
              properties:
                text:
                  type: string
                remindAt:
                  type: string
                  format: date-time
                taskId:
                  type: integer
                  nullable: true
                  description: Optional task in the same workspace.
      responses:
        "200":
          description: Reminder created
        "400":
          description: Invalid input
  /api/me/reminders/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Personal
      summary: Update a reminder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                text:
                  type: string
                remindAt:
                  type: string
                  format: date-time
                done:
                  type: boolean
                taskId:
                  type: integer
                  nullable: true
      responses:
        "200":
          description: Reminder updated
        "400":
          description: Invalid input
        "404":
          description: Reminder not found
    delete:
      tags:
        - Personal
      summary: Delete a reminder
      responses:
        "200":
          description: Reminder deleted
        "404":
          description: Reminder not found
  /api/me/todos:
    get:
      tags:
        - Personal
      summary: List the caller's to-dos
      responses:
        "200":
          description: To-dos
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  todos:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Personal
      summary: Create a to-do
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - text
              properties:
                text:
                  type: string
      responses:
        "200":
          description: To-do created
        "400":
          description: Invalid input
  /api/me/todos/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Personal
      summary: Update a to-do
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                text:
                  type: string
                done:
                  type: boolean
                sortOrder:
                  type: integer
      responses:
        "200":
          description: To-do updated
        "400":
          description: Invalid input
        "404":
          description: To-do not found
    delete:
      tags:
        - Personal
      summary: Delete a to-do
      responses:
        "200":
          description: To-do deleted
        "404":
          description: To-do not found
  /api/whiteboards:
    get:
      tags:
        - Canvas
      summary: List whiteboards in the workspace
      responses:
        "200":
          description: Whiteboards
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  whiteboards:
                    type: array
                    items:
                      type: object
                  workspaceId:
                    type: string
                    format: uuid
    post:
      tags:
        - Canvas
      summary: Create a whiteboard
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                projectRef:
                  type: string
                  nullable: true
                scene:
                  type: object
                  description: Serialized Excalidraw scene (capped at 5 MB).
                thumbnail:
                  type: string
                  nullable: true
      responses:
        "201":
          description: Whiteboard created
        "400":
          description: Invalid input
        "413":
          description: Scene exceeds the 5 MB limit
  /api/whiteboards/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Canvas
      summary: Get a whiteboard
      responses:
        "200":
          description: Whiteboard
        "404":
          description: Whiteboard not found
    patch:
      tags:
        - Canvas
      summary: Rename a whiteboard or save its scene
      description: Rename-only when `scene` is omitted and `title` is present; otherwise saves the scene with an optional optimistic-lock guard (`expectedUpdatedAt`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                scene:
                  type: object
                thumbnail:
                  type: string
                  nullable: true
                expectedUpdatedAt:
                  type: string
                  nullable: true
      responses:
        "200":
          description: Whiteboard updated
        "400":
          description: Invalid input
        "404":
          description: Whiteboard not found
        "409":
          description: Optimistic-lock conflict (stale copy)
        "413":
          description: Scene exceeds the 5 MB limit
    delete:
      tags:
        - Canvas
      summary: Delete a whiteboard
      responses:
        "200":
          description: Whiteboard deleted
        "404":
          description: Whiteboard not found
  /api/mindmaps:
    get:
      tags:
        - Canvas
      summary: List mind maps in the workspace
      responses:
        "200":
          description: Mind maps
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  mindmaps:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Canvas
      summary: Create a mind map
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                projectRef:
                  type: string
                  nullable: true
                graph:
                  type: object
                  description: "{ nodes, edges } graph (capped at 3 MB)."
      responses:
        "201":
          description: Mind map created
        "400":
          description: Invalid input
        "413":
          description: Graph payload too large
  /api/mindmaps/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Canvas
      summary: Get a mind map
      responses:
        "200":
          description: Mind map
        "404":
          description: Mind map not found
    patch:
      tags:
        - Canvas
      summary: Rename a mind map or save its graph
      description: Rename-only when `graph` is omitted and `title` is present; otherwise saves the graph with an optional optimistic-lock guard (`expectedUpdatedAt`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                graph:
                  type: object
                expectedUpdatedAt:
                  type: string
                  nullable: true
      responses:
        "200":
          description: Mind map updated
        "400":
          description: Invalid input
        "404":
          description: Mind map not found
        "409":
          description: Concurrent-edit conflict (stale copy)
        "413":
          description: Graph payload too large
    delete:
      tags:
        - Canvas
      summary: Delete a mind map
      responses:
        "200":
          description: Mind map deleted
        "404":
          description: Mind map not found
  /api/shares:
    get:
      tags:
        - Sharing
      summary: List public share links (admin)
      parameters:
        - name: resourceType
          in: query
          schema:
            type: string
            enum:
              - project
              - view
        - name: resourceId
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Share links
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  links:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Sharing
      summary: Mint a public read-only share link (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - resourceType
                - resourceId
              properties:
                resourceType:
                  type: string
                  enum:
                    - project
                    - view
                resourceId:
                  type: string
                expiresAt:
                  type: string
                  format: date-time
                  nullable: true
      responses:
        "201":
          description: Link created (returns the one-time token + absolute URL)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  token:
                    type: string
                  url:
                    type: string
                  link:
                    type: object
        "400":
          description: Invalid input
    patch:
      tags:
        - Sharing
      summary: Revoke a public share link (admin)
      description: Alias of DELETE. Accepts `?id=` or a JSON body `{ id }`.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
      responses:
        "200":
          description: Link revoked
        "400":
          description: Share link id is required
        "404":
          description: Not found or already revoked
    delete:
      tags:
        - Sharing
      summary: Revoke a public share link (admin)
      parameters:
        - name: id
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Link revoked
        "400":
          description: Share link id is required
        "404":
          description: Not found or already revoked
  /api/p/share/{token}:
    get:
      tags:
        - Sharing
      summary: Resolve a public share token (no auth)
      description: Public, unauthenticated, read-only projection of a shared project/view. A single 404 covers every failure (missing/revoked/expired token, deleted resource). Rate-limited and marked noindex.
      security: []
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Sanitized share projection
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  share:
                    type: object
        "404":
          description: Share link is unavailable
        "429":
          description: Rate limit exceeded
  /api/proofing/{fileId}/annotations:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Proofing
      summary: List annotations on an attachment
      responses:
        "200":
          description: Annotations
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  annotations:
                    type: array
                    items:
                      type: object
    post:
      tags:
        - Proofing
      summary: Add an annotation pin to an attachment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - x
                - y
              properties:
                x:
                  type: number
                y:
                  type: number
                page:
                  type: number
                  default: 1
                comment:
                  type: string
                taskId:
                  type: integer
                  nullable: true
      responses:
        "201":
          description: Annotation created
        "400":
          description: Invalid input
        "404":
          description: Attachment not found
  /api/proofing/{fileId}/annotations/{annotationId}:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
      - name: annotationId
        in: path
        required: true
        schema:
          type: string
    patch:
      tags:
        - Proofing
      summary: Edit an annotation comment and/or toggle resolved
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                comment:
                  type: string
                resolved:
                  type: boolean
      responses:
        "200":
          description: Annotation updated
        "400":
          description: Invalid input
        "404":
          description: Annotation not found
    delete:
      tags:
        - Proofing
      summary: Delete an annotation
      responses:
        "200":
          description: Annotation deleted
        "404":
          description: Annotation not found
  /api/projects/folders:
    get:
      tags:
        - Projects
      summary: List project folders
      responses:
        "200":
          description: Folders
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  folders:
                    type: array
                    items:
                      type: object
                  canManage:
                    type: boolean
    post:
      tags:
        - Projects
      summary: Create a project folder (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                color:
                  type: string
                  nullable: true
      responses:
        "200":
          description: Folder created
        "400":
          description: Invalid input
    patch:
      tags:
        - Projects
      summary: Reorder folders or update one folder (admin)
      description: Reorder mode when `order` (string[] of ids) is supplied; otherwise updates the folder identified by `id`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                order:
                  type: array
                  items:
                    type: string
                id:
                  type: string
                name:
                  type: string
                color:
                  type: string
                  nullable: true
      responses:
        "200":
          description: Folders updated
        "400":
          description: Invalid input
        "404":
          description: Folder not found
    delete:
      tags:
        - Projects
      summary: Delete a project folder (admin)
      description: Accepts `?id=` or a JSON body `{ id }`. Projects in the folder fall back to Uncategorized.
      parameters:
        - name: id
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Folder deleted
        "400":
          description: Folder id is required
        "404":
          description: Folder not found
  /api/geocode:
    get:
      tags:
        - Tasks
      summary: Forward-geocode an address (task Map view)
      description: Server-side proxy to OpenStreetMap Nominatim. A null result means the address could not be located (not an error).
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            maxLength: 300
      responses:
        "200":
          description: Geocode result (or null)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  cached:
                    type: boolean
                  result:
                    nullable: true
                    type: object
                    properties:
                      lat:
                        type: number
                      lng:
                        type: number
                      displayName:
                        type: string
        "400":
          description: Missing or too-long address
        "502":
          description: Geocoding service unavailable
  /api/integrations/trello/import/preview:
    post:
      tags:
        - Integrations
      summary: Dry-run a Trello board export (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                content:
                  type: string
                  description: Trello board JSON export.
      responses:
        "200":
          description: Import preview (columns / counts / assignee matches)
        "400":
          description: Could not parse / nothing importable
  /api/integrations/trello/import/run:
    post:
      tags:
        - Integrations
      summary: Import a Trello board export as a new project (admin)
      description: Idempotent — re-importing the same board skips instead of duplicating.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                content:
                  type: string
                projectName:
                  type: string
      responses:
        "200":
          description: Import committed (or skipped if already imported)
        "400":
          description: Could not parse / import failed
  /api/integrations/monday/import/preview:
    post:
      tags:
        - Integrations
      summary: Dry-run a Monday.com export (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                content:
                  type: string
                format:
                  type: string
                  enum:
                    - csv
                    - json
      responses:
        "200":
          description: Import preview (columns / counts / assignee matches)
        "400":
          description: Could not parse / nothing importable
  /api/integrations/monday/import/run:
    post:
      tags:
        - Integrations
      summary: Import a Monday.com export as a new project (admin)
      description: Idempotent — re-importing the same export skips instead of duplicating.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                content:
                  type: string
                format:
                  type: string
                  enum:
                    - csv
                    - json
                projectName:
                  type: string
      responses:
        "200":
          description: Import committed (or skipped if already imported)
        "400":
          description: Could not parse / import failed
  /api/integrations/notion/connect-token:
    post:
      tags:
        - Integrations
      summary: Connect Notion with an internal integration token
      description: Fallback for Notion's OAuth flow — paste an internal integration token (`secret_…` / `ntn_…`). The token is probed against /v1/users/me and persisted. POST and PUT behave identically.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
      responses:
        "200":
          description: Connected (returns the Notion bot user)
        "400":
          description: Missing / malformed token
        "404":
          description: Unknown provider
        "502":
          description: Connection test failed
    put:
      tags:
        - Integrations
      summary: Connect Notion with an internal integration token
      description: Identical to POST.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
      responses:
        "200":
          description: Connected (returns the Notion bot user)
        "400":
          description: Missing / malformed token
        "404":
          description: Unknown provider
        "502":
          description: Connection test failed
  /api/integrations/notion/import:
    get:
      tags:
        - Integrations
      summary: List the Notion databases the connection can see
      responses:
        "200":
          description: Databases (for the import picker)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  databases:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        title:
                          type: string
        "412":
          description: Notion is not connected
        "502":
          description: Failed to list databases
    post:
      tags:
        - Integrations
      summary: Import a Notion database into a project as tasks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - databaseId
                - projectRef
              properties:
                databaseId:
                  type: string
                projectRef:
                  type: string
                limit:
                  type: integer
      responses:
        "200":
          description: Import report
        "400":
          description: Invalid input / import error
        "404":
          description: Unknown provider
        "412":
          description: Notion is not connected
        "502":
          description: Import failed
  /api/people/profile:
    get:
      tags:
        - People
      summary: List member experience profiles, or fetch one in full
      description: |
        Without `member`, returns every member's profile with `resumeText` elided. With `member`, returns that member's full profile including the resume text. Profiles are keyed by member display name (matching `app_tasks.assigned_to`), not user id, so roster-only members are covered.
      parameters:
        - name: member
          in: query
          schema:
            type: string
          description: Return one member's full profile by display name.
      responses:
        "200":
          description: Profile
          or the elided profile list: null
        "400":
          description: Workspace required
        "401":
          description: Unauthorized
        "403":
          description: Requires the invites/view permission
    put:
      tags:
        - People
      summary: Partial-update a member's experience profile
      description: |
        Omitted fields keep their stored value; an empty string clears a field. When the resume or experience text changes and `deriveSkills` is not false, the profile text is mapped onto the canonical categorized skill vocabulary and merged additively into the member's declared skills (an LLM pass never wipes hand-declared skills).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - memberName
              properties:
                memberName:
                  type: string
                  minLength: 1
                  maxLength: 200
                linkedinUrl:
                  type: string
                  description: Must be a valid http(s) URL.
                experienceSummary:
                  type: string
                  description: Free-text background blurb.
                resumeText:
                  type: string
                  description: Resume as TEXT (extracted client-side from PDF/txt/md), not a file.
                resumeFileName:
                  type: string
                deriveSkills:
                  type: boolean
                  default: true
                  description: Derive declared skills from the profile text after saving.
      responses:
        "200":
          description: Updated profile plus any derivedSkills
        "400":
          description: Invalid body or LinkedIn URL
        "401":
          description: Unauthorized
        "403":
          description: Requires the invites/create permission
  /api/people/team-context:
    get:
      tags:
        - People
      summary: Get the workspace team context brief used for AI assignment
      description: |
        A standing free-text block (who is strong at what, hiring gaps, assignment conventions) injected into the AI agent's system prompt. Any member may read it.
      responses:
        "200":
          description: Team context
        "400":
          description: Workspace required
        "401":
          description: Unauthorized
    put:
      tags:
        - People
      summary: Replace the workspace team context brief (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - context
              properties:
                context:
                  type: string
      responses:
        "200":
          description: Team context saved
        "400":
          description: Invalid body
        "401":
          description: Unauthorized
        "403":
          description: Admin only
  /api/profile/experience:
    get:
      tags:
        - Personal
      summary: Get your own experience profile and declared skills
      description: |
        Self-service sibling of `/api/people/profile` — no people-management permission required. The member name is resolved SERVER-SIDE from the session, never from the request, so a caller can only reach their own row.
      responses:
        "200":
          description: Your profile and skills
        "400":
          description: Workspace required
        "401":
          description: Unauthorized
    put:
      tags:
        - Personal
      summary: Update your own experience profile (and optionally declared skills)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                linkedinUrl:
                  type: string
                experienceSummary:
                  type: string
                resumeText:
                  type: string
                resumeFileName:
                  type: string
                skills:
                  type: array
                  description: Replaces your declared skill set (same shape as /api/people/skills).
                  items:
                    type: object
                    required:
                      - skill
                      - strength
                    properties:
                      skill:
                        type: string
                        description: A canonical vocabulary key, or your own custom free-text skill.
                      strength:
                        type: number
                        minimum: 0
                        maximum: 1
                deriveSkills:
                  type: boolean
                  default: true
      responses:
        "200":
          description: Updated profile plus any derivedSkills
        "400":
          description: Invalid body
        "401":
          description: Unauthorized
  /api/ai-agent/activity:
    get:
      tags:
        - AI Agent
      summary: Agent activity timeline — individual ledger rows (admin)
      description: |
        One row per agent write, newest first. This is the row-level companion to /api/ai-agent/activity-report, which returns only aggregates ("47 actions, 3 approvals") and so answers how much the agent did but never what.
        Admin-only: the ledger spans every member's agent use.
        Opt-in — returns 403 `agent_oversight_disabled` unless the workspace has enabled the activity ledger viewer. The ledger keeps RECORDING either way; the flag only governs whether the workspace has asked to see it.
        Paginates by keyset cursor on `created_at` (`before`), which is stable under concurrent writes where an offset would not be. `next_cursor` is null on the last page. Unknown filter values are ignored rather than rejected, so a stale bookmark shows the unfiltered timeline.
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: before
          in: query
          description: Keyset cursor — return rows older than this ISO timestamp.
          schema:
            type: string
            format: date-time
        - name: status
          in: query
          schema:
            type: string
            enum:
              - executed
              - parked
              - blocked
              - failed
              - undone
        - name: run_kind
          in: query
          schema:
            type: string
            enum:
              - interactive
              - autonomous
              - scheduled
        - name: category
          in: query
          description: Action-policy category (external_send, finance, destructive, …).
          schema:
            type: string
        - name: user_id
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Actions plus `next_cursor` (null on the last page)
        "400":
          description: Workspace required
        "401":
          description: Unauthorized
        "403":
          description: Admin only, or the activity ledger is disabled (`agent_oversight_disabled`)
  /api/ai-agent/approvals:
    get:
      tags:
        - AI Agent
      summary: Global agent approvals inbox (admin)
      description: |
        Every action the agent has parked for a human, workspace-wide, plus the pending count.
        This is the only route that can surface an approval from an event-driven run (a new email, a PR), which has no task to navigate to. Deliberately NOT gated on the oversight opt-in for that reason — gating it would strand those approvals in workspaces that had not opted in.
        Admin-only. The rows enumerate every outbound email and payment the agent is holding; `tool_input` is stripped from the payload, but the inventory itself is privileged and only admins can resolve an approval anyway.
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
      responses:
        "200":
          description: Parked approvals plus the pending count
        "400":
          description: Workspace required
        "401":
          description: Unauthorized
        "403":
          description: Admin only
  /api/ai-agent/approvals/{approvalId}:
    post:
      tags:
        - AI Agent
      summary: Approve or deny a parked agent action (admin)
      description: |
        Resolve a parked action from the global inbox — the task-independent sibling of /api/tasks/{id}/agent-run/approvals/{approvalId}, and the only way to resolve an approval from an event-driven run.
        Approving means EXECUTING the action (sending the email, moving the money), and the actions that park are exactly the dangerous ones — so this requires an admin, which is stricter than the task-scoped route's `tasks:edit`.
        Shares the approvals service with the task-scoped route, so both re-check expiry, guardrails, and policy at resolve time rather than trusting the decision made when the action was parked.
      parameters:
        - name: approvalId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - decision
              properties:
                decision:
                  type: string
                  enum:
                    - approve
                    - deny
      responses:
        "200":
          description: Resolved — the outcome of the execution (or the denial)
        "400":
          description: Workspace required
          or an invalid decision: null
        "401":
          description: Unauthorized
        "403":
          description: Admin only
        "404":
          description: No actionable approval with that id
        "409":
          description: Blocked on re-check — a guardrail or policy refused it at resolve time
        "410":
          description: The approval expired
        "502":
          description: The approved action failed to execute
  /api/ai-agent/outcomes:
    get:
      tags:
        - AI Agent
      summary: Agent outcome checks and hit rate (admin)
      description: |
        "Did the agent's work actually land?" The activity ledger answers what the agent DID; this answers whether it WORKED. Each row is a claim the agent made about the world, settled against the database by the verify-outcomes cron — met, missed, or cancelled when the referenced thing vanished and the claim became undecidable.
        The hit rate counts only SETTLED checks: pending ones are not evidence yet, and cancelled ones are not evidence either way.
        Opt-in — returns 403 `agent_oversight_disabled` unless outcome verification is enabled for the workspace.
      parameters:
        - name: days
          in: query
          description: Scorecard window. Default 30.
          schema:
            type: integer
            default: 30
            minimum: 1
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - met
              - missed
              - cancelled
      responses:
        "200":
          description: Expectations, the scorecard, and `escalate_on_failure`
        "401":
          description: Unauthorized
        "403":
          description: Admin only, or outcome verification is disabled (`agent_oversight_disabled`)
    post:
      tags:
        - AI Agent
      summary: Cancel a pending outcome check (admin)
      description: |
        Exclude a pending check a human judges unfair — the task was descoped, the person left — rather than letting it count as a miss.
        Audited (`ai_agent.outcome_check_cancelled`), because "cancel the check that was about to fail" is precisely how this ledger could be laundered into looking clean.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - expectation_id
              properties:
                expectation_id:
                  type: string
                  minLength: 1
                  maxLength: 64
      responses:
        "200":
          description: Cancelled
        "400":
          description: Invalid body
        "401":
          description: Unauthorized
        "403":
          description: Admin only, or outcome verification is disabled (`agent_oversight_disabled`)
        "404":
          description: No pending outcome check with that id
  /api/ai-agent/action-policies:
    get:
      tags:
        - AI Agent
      summary: List agent action policies and the category catalog (admin)
      description: |
        Per-category policy governing risky agent actions. Categories are external_send, finance, destructive, membership, deploy, external_write. No row for a category means "park for approval" (fail closed).
      responses:
        "200":
          description: Policies plus the category catalog
        "401":
          description: Unauthorized
        "403":
          description: Admin only
    put:
      tags:
        - AI Agent
      summary: Bulk upsert agent action policies (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - policies
              properties:
                policies:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    type: object
                    required:
                      - category
                      - mode
                    properties:
                      persona_id:
                        type: string
                        nullable: true
                        description: Persona-scoped policy. A persona row shadows the workspace row.
                      category:
                        type: string
                        enum:
                          - external_send
                          - finance
                          - destructive
                          - membership
                          - deploy
                          - external_write
                      mode:
                        type: string
                        enum:
                          - allow
                          - allow_budget
                          - approve
                          - block
                      daily_limit:
                        type: integer
                        minimum: 1
                        maximum: 1000
                        description: Required when mode is allow_budget — executed actions per UTC day.
      responses:
        "200":
          description: Saved policies
        "400":
          description: Unknown category/mode
          or a missing daily_limit: null
        "401":
          description: Unauthorized
        "403":
          description: Admin only
    delete:
      tags:
        - AI Agent
      summary: Delete an agent action policy (admin)
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
        "400":
          description: Missing id
        "401":
          description: Unauthorized
        "403":
          description: Admin only
        "404":
          description: Policy not found
  /api/ai-agent/escalation-policy:
    get:
      tags:
        - AI Agent
      summary: Get the workspace agent escalation policy (admin)
      responses:
        "200":
          description: Escalation policy
        "401":
          description: Unauthorized
        "403":
          description: Admin only
    put:
      tags:
        - AI Agent
      summary: Update the workspace agent escalation policy (admin)
      description: |
        Partial update — omitted fields keep their current value. Recipients are validated against active workspace membership; `notify_user_ids: null` means "all admins".
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                min_immediate_severity:
                  type: string
                  enum:
                    - info
                    - warning
                    - critical
                quiet_hours_enabled:
                  type: boolean
                quiet_start_hour:
                  type: integer
                  minimum: 0
                  maximum: 23
                quiet_end_hour:
                  type: integer
                  minimum: 0
                  maximum: 23
                timezone:
                  type: string
                  description: IANA timezone, e.g. America/Los_Angeles.
                critical_overrides_quiet_hours:
                  type: boolean
                notify_user_ids:
                  type: array
                  nullable: true
                  items:
                    type: string
                    format: uuid
      responses:
        "200":
          description: Saved policy
        "400":
          description: No provided recipient is an active member
        "401":
          description: Unauthorized
        "403":
          description: Admin only
  /api/ai-agent/guardrails:
    get:
      tags:
        - AI Agent
      summary: List agent guardrail rules and the catalog (admin)
      parameters:
        - name: persona_id
          in: query
          schema:
            type: string
          description: Filter to one persona's rules.
      responses:
        "200":
          description: Rules plus the guardrail catalog
        "401":
          description: Unauthorized
        "403":
          description: Admin only
    post:
      tags:
        - AI Agent
      summary: Create an agent guardrail rule (admin)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - pattern
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                pattern:
                  type: string
                  minLength: 1
                  maxLength: 300
                rule_type:
                  type: string
                  enum:
                    - recipient
                    - keyword
                  default: recipient
                match_mode:
                  type: string
                  enum:
                    - exact
                    - domain
                    - contains
                  default: exact
                action:
                  type: string
                  enum:
                    - block
                    - approve
                  default: block
                reason:
                  type: string
                  maxLength: 300
                persona_id:
                  type: string
                  description: Scope the rule to one persona.
      responses:
        "200":
          description: Created rule
        "400":
          description: Invalid body or unknown persona
        "401":
          description: Unauthorized
        "403":
          description: Admin only
    patch:
      tags:
        - AI Agent
      summary: Update an agent guardrail rule (admin)
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                pattern:
                  type: string
                  minLength: 1
                  maxLength: 300
                rule_type:
                  type: string
                  enum:
                    - recipient
                    - keyword
                match_mode:
                  type: string
                  enum:
                    - exact
                    - domain
                    - contains
                action:
                  type: string
                  enum:
                    - block
                    - approve
                enabled:
                  type: boolean
      responses:
        "200":
          description: Updated rule
        "400":
          description: Invalid field
        "401":
          description: Unauthorized
        "403":
          description: Admin only
        "404":
          description: Rule not found
    delete:
      tags:
        - AI Agent
      summary: Delete an agent guardrail rule (admin)
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
        "400":
          description: Missing id
        "401":
          description: Unauthorized
        "403":
          description: Admin only
        "404":
          description: Rule not found
  /api/ai-agent/activity-report:
    get:
      tags:
        - AI Agent
      summary: Agent activity/self-report — totals, categories, top tools, approvals (admin)
      parameters:
        - name: days
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 90
            default: 7
          description: Lookback window in days (clamped to 90).
      responses:
        "200":
          description: Activity report
        "401":
          description: Unauthorized
        "403":
          description: Admin only
  /api/ai-agent/schedules/runs:
    get:
      tags:
        - AI Agent
      summary: List scheduled autonomous agent runs
      parameters:
        - name: schedule
          in: query
          schema:
            type: string
          description: Filter to one schedule id.
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
      responses:
        "200":
          description: Runs, the schedules they belong to, and whether the caller can manage them
        "401":
          description: Unauthorized
  /api/ai-agent/share:
    post:
      tags:
        - AI Agent
      summary: Share an agent answer or conversation
      description: |
        Shares a single message or a whole conversation. `destination=knowledge` saves it as a knowledge document; `destination=snapshot` creates a snapshot that can additionally be DM'd to recipients or made public via a link.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                scope:
                  type: string
                  enum:
                    - message
                    - conversation
                  default: message
                destination:
                  type: string
                  enum:
                    - snapshot
                    - knowledge
                  default: snapshot
                visibility:
                  type: string
                  enum:
                    - workspace
                    - public
                  default: workspace
                title:
                  type: string
                note:
                  type: string
                content:
                  type: string
                  description: Required for a message-scope snapshot.
                source_conversation_id:
                  type: string
                recipient_user_ids:
                  type: array
                  maxItems: 20
                  items:
                    type: string
                    format: uuid
                  description: DM the snapshot to these members (active members of this workspace only).
      responses:
        "200":
          description: Snapshot (with share url when public) or the created knowledge document
        "400":
          description: Invalid body / missing content / invalid recipients
        "401":
          description: Unauthorized
        "404":
          description: Conversation not found
        "501":
          description: No LLM configured
  /api/ai-agent/share/{id}:
    get:
      tags:
        - AI Agent
      summary: Read a shared agent snapshot
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Snapshot
        "401":
          description: Unauthorized
        "404":
          description: Not found
  /api/integrations/gmail/inbox/status:
    get:
      tags:
        - Integrations
      summary: Gmail inbox connection status
      description: |
        Resolves whichever Gmail backend the user has connected — the AngelSend bridge first, then native Google OAuth. `has_gmail_read: false` on a connected account means the token predates the gmail.readonly scope and the user must reconnect (not connect).
      responses:
        "200":
          description: Connection status, backend, and read-scope flag
        "401":
          description: Unauthorized
  /api/integrations/gmail/inbox/messages:
    get:
      tags:
        - Integrations
      summary: List Gmail messages, or fetch one with its body
      parameters:
        - name: id
          in: query
          schema:
            type: string
          description: Fetch a single message including its body.
        - name: q
          in: query
          schema:
            type: string
          description: Gmail search query.
        - name: maxResults
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: pageToken
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Message envelopes
          or a single message: null
        "401":
          description: Unauthorized
        "412":
          description: No Gmail backend connected
  /api/integrations/gmail/inbox/send:
    post:
      tags:
        - Integrations
      summary: Send an email through the connected Gmail backend
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - to
                - subject
                - body
              properties:
                to:
                  oneOf:
                    - type: string
                    - type: array
                      items:
                        type: string
                subject:
                  type: string
                body:
                  type: string
                from:
                  type: string
                cc:
                  type: array
                  items:
                    type: string
                bcc:
                  type: array
                  items:
                    type: string
                is_html:
                  type: boolean
                thread_id:
                  type: string
                  description: Reply into an existing thread.
      responses:
        "200":
          description: Sent
        "400":
          description: Missing to/subject/body
        "401":
          description: Unauthorized
        "412":
          description: No Gmail backend connected
  /api/integrations/icloud/messages:
    get:
      tags:
        - Integrations
      summary: List synced iCloud Mail envelopes
      description: Envelope-only (IMAP). Bodies are not synced.
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        "200":
          description: Message envelopes
        "401":
          description: Unauthorized
        "412":
          description: iCloud is not connected
    post:
      tags:
        - Integrations
      summary: Send an email via iCloud SMTP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - to
                - subject
                - body
              properties:
                to:
                  type: string
                subject:
                  type: string
                body:
                  type: string
                cc:
                  type: string
                bcc:
                  type: string
                skipDefaultBcc:
                  type: boolean
      responses:
        "200":
          description: Sent
        "400":
          description: Missing to/subject/body
        "401":
          description: Unauthorized
        "412":
          description: iCloud is not connected
  /api/integrations/icloud/events:
    get:
      tags:
        - Integrations
      summary: List iCloud calendar events (read live over CalDAV)
      parameters:
        - name: timeMin
          in: query
          schema:
            type: string
            format: date-time
          description: "Alias: from."
        - name: timeMax
          in: query
          schema:
            type: string
            format: date-time
          description: "Alias: to."
      responses:
        "200":
          description: Events
        "401":
          description: Unauthorized
        "412":
          description: iCloud is not connected
        "502":
          description: CalDAV read failed
    post:
      tags:
        - Integrations
      summary: Create an iCloud calendar event
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - summary
                - start
                - end
              properties:
                summary:
                  type: string
                start:
                  type: string
                  format: date-time
                end:
                  type: string
                  format: date-time
                location:
                  type: string
                description:
                  type: string
      responses:
        "200":
          description: Created event
        "400":
          description: Missing summary/start/end
        "401":
          description: Unauthorized
        "412":
          description: iCloud is not connected
components:
  securitySchemes:
    sessionCookie:
      type: apiKey
      in: cookie
      name: kavanah_session
      description: JWT session token set on login/signup
    apiKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: kvn_live_…
      description: |
        Workspace API key minted by an admin under Settings → API keys, sent as `Authorization: Bearer kvn_live_…`. Bound to the (workspace, user) that created it; requests act as that user. API-key requests are exempt from the CSRF cookie/header pair that session-cookie requests need. The plaintext token is shown once at creation and cannot be retrieved again.
    appKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: kvn_app_…
      description: |
        Community-app key, minted per install under /apps → Installed → API keys, sent as `Authorization: Bearer kvn_app_…`.
        An app key authenticates as the INSTALLED APP, not as the user who minted it: every RBAC check fails closed, so authorization comes exclusively from the scopes granted at install time. Effective scopes are re-derived on every request as (the key's minted scopes ∩ the install's current granted scopes), so narrowing or disabling an install immediately narrows every key it issued.
        App keys are REJECTED WITH 403 on every operation that does not list this scheme — the callable surface is exactly the operations carrying an `x-app-scope` value. Within that surface an app still sees only what a plain member of the workspace sees (private-project visibility applies), and no DELETE operation is exposed to apps: destructive work goes through an agent tool, where the workspace action policy governs it.
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
      required:
        - error
    TaskComment:
      type: object
      description: "A task comment. The shape is intentionally just id + body: any PATCH that includes `comments` rebuilds every entry as {id, body}, so extra fields stored here would be dropped on the next board edit."
      properties:
        id:
          type: string
        body:
          type: string
      required:
        - id
        - body
    Task:
      type: object
      properties:
        id:
          type: integer
        workspace_id:
          type: string
          format: uuid
        project_ref:
          type: string
        status_id:
          type: string
        title:
          type: string
        description:
          type: string
        tag:
          type: string
        priority:
          type: string
          enum:
            - Normal
            - Medium
            - High
            - Done
        assigned_to:
          type: string
          nullable: true
        due_date:
          type: string
          format: date
          nullable: true
        reminder_at:
          type: string
          format: date-time
          nullable: true
        reminder_date:
          type: string
          format: date
          nullable: true
        created_by:
          type: string
        subtasks:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              title:
                type: string
              completed:
                type: boolean
        notes:
          type: array
          items:
            type: string
        links:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              url:
                type: string
        comments:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              body:
                type: string
        file_ids:
          type: array
          items:
            type: string
        sort_order:
          type: integer
        created_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
    Client:
      type: object
      properties:
        id:
          type: string
        workspace_id:
          type: string
          format: uuid
        company:
          type: string
        contact_name:
          type: string
        contact_role:
          type: string
        email:
          type: string
          format: email
        location:
          type: string
        website:
          type: string
        logo_url:
          type: string
          nullable: true
        owner_name:
          type: string
        owner_initials:
          type: string
        owner_tone:
          type: string
          enum:
            - sand
            - rose
            - olive
            - slate
            - peach
        stage:
          type: string
          enum:
            - Onboarding
            - Active
            - Expansion
            - Paused
        health:
          type: string
          enum:
            - Healthy
            - Watch
            - At Risk
        invoice_status:
          type: string
          enum:
            - Paid
            - Pending
            - Overdue
        active_projects:
          type: integer
        arr:
          type: number
        last_activity:
          type: string
        next_renewal:
          type: string
          format: date
          nullable: true
        priority:
          type: boolean
        archived:
          type: boolean
        employees:
          type: array
          items:
            type: object
        created_at:
          type: string
          format: date-time
    ProjectWorkspaceBundle:
      type: object
      properties:
        projects:
          type: array
          items:
            type: object
        teamMembers:
          type: array
          items:
            type: object
        workspaceTeams:
          type: array
          items:
            type: object
        notifications:
          type: object
        integrations:
          type: object
    ChatMessage:
      type: object
      properties:
        id:
          type: string
        content:
          type: string
        sender:
          type: string
          enum:
            - me
            - them
        time:
          type: string
        type:
          type: string
          enum:
            - text
            - attachment
            - missed-call
        attachments:
          type: array
          items:
            type: object
    ActivityItem:
      type: object
      properties:
        name:
          type: string
        action:
          type: string
        detail:
          type: string
        tone:
          type: string
          enum:
            - sand
            - rose
            - olive
            - slate
            - peach
        initials:
          type: string
        status:
          type: string
          enum:
            - online
            - busy
            - neutral
    ProfileFormState:
      type: object
      properties:
        firstName:
          type: string
        lastName:
          type: string
        fullName:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
        jobTitle:
          type: string
        department:
          type: string
        bio:
          type: string
        avatarTone:
          type: string
    Swimlane:
      type: object
      properties:
        id:
          type: string
        workspace_id:
          type: string
          format: uuid
        project_ref:
          type: string
        name:
          type: string
        position:
          type: integer
        color:
          type: string
          nullable: true
        archived_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - workspace_id
        - project_ref
        - name
        - position
    ColumnLimit:
      type: object
      properties:
        workspace_id:
          type: string
          format: uuid
        project_ref:
          type: string
        status_id:
          type: string
        wip_limit:
          type: integer
          nullable: true
          minimum: 1
          maximum: 1000
        name_override:
          type: string
          nullable: true
          maxLength: 64
        updated_at:
          type: string
          format: date-time
      required:
        - workspace_id
        - project_ref
        - status_id
    TaskTransition:
      type: object
      properties:
        id:
          type: string
        workspace_id:
          type: string
          format: uuid
        task_id:
          type: integer
        from_status:
          type: string
          nullable: true
        to_status:
          type: string
        moved_by_user_id:
          type: string
          nullable: true
        moved_at:
          type: string
          format: date-time
        ms_in_prev_status:
          type: integer
          nullable: true
          description: Milliseconds the task spent in from_status. Null on the first transition.
      required:
        - id
        - workspace_id
        - task_id
        - to_status
        - moved_at
    StatusTiming:
      type: object
      description: Aggregate cycle-time stats for one status on one project.
      properties:
        status:
          type: string
        count:
          type: integer
        avg_ms:
          type: integer
        p50_ms:
          type: integer
        p90_ms:
          type: integer
      required:
        - status
        - count
        - avg_ms
        - p50_ms
        - p90_ms
    IntegrationProvider:
      type: object
      description: A row in the public integration catalog.
      properties:
        slug:
          type: string
          description: Stable identifier used in URLs (e.g. `gmail`, `slack`).
        name:
          type: string
        description:
          type: string
        category:
          type: string
          description: Free-form group label (e.g. `communication`, `storage`, `calendar`, `project-management`).
        authType:
          type: string
          enum:
            - api_key
            - oauth2
        capabilities:
          type: array
          items:
            type: string
          description: Capability tags surfaced as badges in Settings → Integrations.
        docsUrl:
          type: string
          nullable: true
        baseUrl:
          type: string
          nullable: true
        enabled:
          type: boolean
      required:
        - slug
        - name
        - description
        - category
        - authType
        - capabilities
        - enabled
    IntegrationConnection:
      type: object
      description: A per-user credential row. Never exposes the credential itself.
      properties:
        providerSlug:
          type: string
        status:
          type: string
          enum:
            - connected
            - disconnected
            - error
        lastStatusReason:
          type: string
          nullable: true
        metadata:
          type: object
          description: |
            Provider-specific display metadata: accountEmail, accountName,
            externalWorkspace, scope, expiresAt, etc. Free-form jsonb.
          additionalProperties: true
        connectedAt:
          type: string
          format: date-time
        lastUsedAt:
          type: string
          format: date-time
          nullable: true
      required:
        - providerSlug
        - status
        - connectedAt
  responses:
    BadRequest:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: Missing or invalid session
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Forbidden:
      description: Authenticated but lacking the required permission
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
