{
  "openapi": "3.0.3",
  "info": {
    "title": "Hourivo API",
    "description": "RESTful API for Hourivo — AI-powered workforce management, time tracking, screenshot monitoring, project profitability, and invoicing for agencies and remote teams.\n\n## Authentication\nProtected endpoints require HTTP Bearer JWT (`Authorization: Bearer <token>`) and `x-organization-id` for multi-tenant isolation. See /docs/auth.\n\n## Public endpoints\n- GET /health\n- GET /info\n- GET /projects/portal/{token}\n\n## Rate Limits\n120 requests per minute per IP.\n\n## Errors\nEvery error is JSON: { success: false, code, error, message, hint }.",
    "version": "1.0.0",
    "contact": {
      "name": "Hourivo Support",
      "url": "https://hourivo.com/contact",
      "email": "support@hourivo.com"
    },
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    }
  },
  "servers": [
    {
      "url": "/api/v1",
      "description": "Same-origin (this site)"
    },
    {
      "url": "https://hourivo.com/api/v1",
      "description": "Canonical production"
    },
    {
      "url": "https://projectmang-black.vercel.app/api/v1",
      "description": "Public Vercel deployment"
    }
  ],
  "tags": [
    {
      "name": "Health",
      "description": "Service health and info endpoints"
    },
    {
      "name": "Auth",
      "description": "Authentication, registration, and user management"
    },
    {
      "name": "Organizations",
      "description": "Organization and member management"
    },
    {
      "name": "Projects",
      "description": "Project CRUD and client portal"
    },
    {
      "name": "Time Tracking",
      "description": "Time entries, timers, and timesheets"
    },
    {
      "name": "Screenshots",
      "description": "Screenshot capture and proof-of-work"
    },
    {
      "name": "Activities",
      "description": "App usage and activity summaries"
    },
    {
      "name": "Financials",
      "description": "Profitability, invoicing, and billing"
    },
    {
      "name": "AI",
      "description": "AI-powered summaries and chat"
    },
    {
      "name": "Notifications",
      "description": "In-app notification system"
    },
    {
      "name": "RBAC",
      "description": "Role-based access control"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Health check",
        "description": "Returns the current health status of the Hourivo API. Use this to verify the service is running and responsive. No authentication required.",
        "tags": [
          "Health"
        ],
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/info": {
      "get": {
        "operationId": "getApiInfo",
        "summary": "API information",
        "description": "Returns metadata about the API including version, documentation links, and available resources. Useful for agent discovery.",
        "tags": [
          "Health"
        ],
        "responses": {
          "200": {
            "description": "API information",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "example": "Hourivo API"
                    },
                    "version": {
                      "type": "string",
                      "example": "1.0.0"
                    },
                    "description": {
                      "type": "string"
                    },
                    "documentation": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapiSpec": {
                      "type": "string",
                      "format": "uri"
                    },
                    "health": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/register": {
      "post": {
        "operationId": "registerUser",
        "summary": "Register a new user",
        "description": "Create a new user account and organization. Requires email OTP verification first via /auth/send-otp and /auth/verify-otp. New accounts must send acceptedTerms true to accept the current Terms of Service and Privacy Policy.",
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "fullName",
                  "organizationName",
                  "password",
                  "otp",
                  "acceptedTerms"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "User email address"
                  },
                  "fullName": {
                    "type": "string",
                    "description": "Full display name"
                  },
                  "organizationName": {
                    "type": "string",
                    "description": "Agency or company name"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "description": "Account password (minimum 8 characters)"
                  },
                  "otp": {
                    "type": "string",
                    "description": "OTP code from email verification"
                  },
                  "acceptedTerms": {
                    "type": "boolean",
                    "description": "Must be true for a new account. Accepts current Terms of Service and Privacy Policy (see GET /auth/legal)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "409": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/auth/login": {
      "post": {
        "operationId": "loginUser",
        "summary": "Login to existing account",
        "description": "Authenticate with email and password to receive a JWT token. The user object includes needsLegalAcceptance; if true, call POST /auth/accept-terms with the version from GET /auth/legal before using the product.",
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful, returns JWT token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/auth/methods": {
      "get": {
        "operationId": "getAuthMethods",
        "summary": "List available auth methods",
        "description": "Returns whether email OTP and Google sign-in are enabled for this deployment.",
        "tags": [
          "Auth"
        ],
        "responses": {
          "200": {
            "description": "Email OTP and Google availability",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/auth/oauth": {
      "post": {
        "operationId": "oauthExchange",
        "summary": "Exchange a Google/Supabase access token for an app JWT",
        "description": "Verifies a Supabase Google session and issues a Hourivo JWT. New users must send acceptedTerms true.",
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "accessToken"
                ],
                "properties": {
                  "accessToken": {
                    "type": "string"
                  },
                  "acceptedTerms": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/auth/send-otp": {
      "post": {
        "operationId": "sendEmailOtp",
        "summary": "Send email OTP",
        "description": "Send a one-time password to the specified email address for verification during registration.",
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OTP sent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/auth/verify-otp": {
      "post": {
        "operationId": "verifyEmailOtp",
        "summary": "Verify email OTP",
        "description": "Verify the one-time password sent to the user's email address.",
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "otp"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "otp": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OTP verified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "operationId": "getCurrentUser",
        "summary": "Get current user profile",
        "description": "Returns the authenticated user's profile including name, email, role, termsVersion, and needsLegalAcceptance.",
        "tags": [
          "Auth"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "User profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/auth/legal": {
      "get": {
        "operationId": "getLegalNotice",
        "summary": "Current Terms and Privacy version",
        "description": "Public endpoint that returns the current Terms of Service and Privacy Policy version, effective date, and public URLs. No authentication required.",
        "tags": [
          "Auth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Current legal documents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/auth/accept-terms": {
      "post": {
        "operationId": "acceptLegalTerms",
        "summary": "Accept current Terms and Privacy Policy",
        "description": "Records that the authenticated user accepted the current Terms of Service and Privacy Policy. Clients must send the published version; stale versions are rejected.",
        "tags": [
          "Auth"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "termsVersion": {
                    "type": "string",
                    "example": "2026.8",
                    "description": "Must match the current published legal version from GET /auth/legal."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acceptance recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/auth/accept-invite": {
      "post": {
        "operationId": "acceptInvite",
        "summary": "Accept an organization invite",
        "description": "Join an organization with an invite token and password. acceptedTerms must be true to accept the current Terms of Service and Privacy Policy.",
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "password",
                  "acceptedTerms"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Invite token from the invitation email or /invite URL."
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "description": "Password for the new or existing invited user."
                  },
                  "fullName": {
                    "type": "string",
                    "description": "Optional display name."
                  },
                  "acceptedTerms": {
                    "type": "boolean",
                    "description": "Must be true."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Invite accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/organizations/members": {
      "get": {
        "operationId": "listOrganizationMembers",
        "summary": "List organization members",
        "description": "Returns all members of the current organization with their roles, rates, and status.",
        "tags": [
          "Organizations"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of organization members",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "addOrganizationMember",
        "summary": "Invite a new member",
        "description": "Add a new member to the organization by email. Requires Admin or Owner role.",
        "tags": [
          "Organizations"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "fullName",
                  "roleId"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "fullName": {
                    "type": "string"
                  },
                  "roleId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "password": {
                    "type": "string"
                  },
                  "costRate": {
                    "type": "number",
                    "minimum": 0
                  },
                  "billableRate": {
                    "type": "number",
                    "minimum": 0
                  },
                  "weeklyCapacityHours": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 168
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Member added",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "operationId": "listProjects",
        "summary": "List all projects",
        "description": "Returns all projects in the current organization with budgets, assigned members, and profitability data.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of projects",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createProject",
        "summary": "Create a new project",
        "description": "Create a new project with budget, billing rate, and screenshot settings.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "clientName",
                  "code",
                  "totalBudget",
                  "screenshotIntervalMins"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Project name"
                  },
                  "clientName": {
                    "type": "string",
                    "description": "Client or company name"
                  },
                  "code": {
                    "type": "string",
                    "description": "Short project code (e.g., PROJ-01)"
                  },
                  "description": {
                    "type": "string"
                  },
                  "budgetType": {
                    "type": "string",
                    "enum": [
                      "FIXED",
                      "HOURLY"
                    ],
                    "default": "FIXED"
                  },
                  "totalBudget": {
                    "type": "number",
                    "minimum": 0
                  },
                  "hourlyBillRate": {
                    "type": "number",
                    "minimum": 0
                  },
                  "screenshotIntervalMins": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 60,
                    "description": "Minutes between automatic screenshots"
                  },
                  "blurScreenshots": {
                    "type": "boolean",
                    "default": false
                  },
                  "assignedUserIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/projects/{projectId}": {
      "put": {
        "operationId": "updateProject",
        "summary": "Update a project",
        "description": "Update project settings, budget, members, or screenshot configuration.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter projectId for updateProject"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Project updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/share": {
      "post": {
        "operationId": "shareProjectPortal",
        "summary": "Generate client portal link",
        "description": "Create or update a shareable public client portal for a project. Returns a unique token URL.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter projectId for shareProjectPortal"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "isPublicShared": {
                    "type": "boolean"
                  },
                  "sharePassword": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Portal link generated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/projects/portal/{token}": {
      "get": {
        "operationId": "getPublicPortalData",
        "summary": "Get public portal data",
        "description": "Retrieve project proof-of-work data via a public portal token. No authentication required — the token is the authorization.",
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "path parameter token for getPublicPortalData"
          },
          {
            "name": "password",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "query parameter password for getPublicPortalData"
          }
        ],
        "responses": {
          "200": {
            "description": "Portal data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/time/entries": {
      "get": {
        "operationId": "listTimeEntries",
        "summary": "List time entries",
        "description": "Retrieve time entries with optional filters for user, project, and date range. Returns paginated results.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "query parameter userId for listTimeEntries"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            },
            "description": "query parameter limit for listTimeEntries"
          }
        ],
        "responses": {
          "200": {
            "description": "Time entries list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/time/entries/{entryId}": {
      "put": {
        "operationId": "updateTimeEntry",
        "summary": "Update a time entry",
        "description": "Update a manual time entry's details including project, notes, and billable status.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "entryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter entryId for updateTimeEntry"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "startTime": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endTime": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "isBillable": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Entry updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteTimeEntry",
        "summary": "Delete a time entry",
        "description": "Permanently remove a time entry. This action cannot be undone.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "entryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter entryId for deleteTimeEntry"
          }
        ],
        "responses": {
          "200": {
            "description": "Entry deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/time/manual": {
      "post": {
        "operationId": "logManualTime",
        "summary": "Log manual time entry",
        "description": "Create a manual time entry for past work. Useful for backfilling time that wasn't tracked automatically.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "startTime",
                  "endTime"
                ],
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "startTime": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endTime": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "isBillable": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Manual entry created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/time/approve": {
      "post": {
        "operationId": "batchApproveTimesheets",
        "summary": "Batch approve/reject timesheets",
        "description": "Approve or reject multiple time entries at once. Requires Manager or Admin role.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "timeEntryIds",
                  "status"
                ],
                "properties": {
                  "timeEntryIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "APPROVED",
                      "REJECTED"
                    ]
                  },
                  "rejectionReason": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 500,
                    "description": "Required when status is REJECTED. Shown to the teammate in-app and by email."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Timesheets updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/time/live-team": {
      "get": {
        "operationId": "getLiveTeam",
        "summary": "Get live team status",
        "description": "Returns real-time online/offline status for all team members with current timer information.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Live team data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/time/idle-resolve": {
      "post": {
        "operationId": "resolveIdleTime",
        "summary": "Resolve smart idle time",
        "description": "Handle idle time detection results — keep as-is, discard, or split into offline segment.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action",
                  "idleSeconds"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "KEEP",
                      "DISCARD",
                      "SPLIT_OFFLINE"
                    ]
                  },
                  "idleSeconds": {
                    "type": "integer"
                  },
                  "timeEntryId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "customNotes": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idle time resolved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/time/weekly-digest": {
      "post": {
        "operationId": "emailWeeklyHoursDigest",
        "summary": "Email this week's hours",
        "description": "Email the current member their hours for this ISO week. Available on every plan. Team-plan Gemini Friday reports are a separate AI email.",
        "tags": [
          "Time Tracking"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Hours digest emailed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/screenshots": {
      "get": {
        "operationId": "listScreenshots",
        "summary": "List screenshots",
        "description": "Retrieve screenshots with optional user and project filters. Returns paginated results with metadata.",
        "tags": [
          "Screenshots"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "query parameter userId for listScreenshots"
          },
          {
            "name": "projectId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "query parameter projectId for listScreenshots"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            },
            "description": "query parameter limit for listScreenshots"
          }
        ],
        "responses": {
          "200": {
            "description": "Screenshots list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/screenshots/{screenshotId}": {
      "delete": {
        "operationId": "deleteScreenshot",
        "summary": "Delete a screenshot",
        "description": "Permanently remove a screenshot and its associated storage file.",
        "tags": [
          "Screenshots"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "screenshotId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter screenshotId for deleteScreenshot"
          }
        ],
        "responses": {
          "200": {
            "description": "Screenshot deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/screenshots/{screenshotId}/blur": {
      "patch": {
        "operationId": "toggleScreenshotBlur",
        "summary": "Toggle screenshot blur",
        "description": "Enable or disable blur on a specific screenshot for privacy.",
        "tags": [
          "Screenshots"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "screenshotId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter screenshotId for toggleScreenshotBlur"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "isBlurred": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Blur status updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/screenshots/request-live-capture": {
      "post": {
        "operationId": "requestLiveCapture",
        "summary": "Request live screenshot",
        "description": "Trigger an immediate screenshot capture on a team member's desktop tracker.",
        "tags": [
          "Screenshots"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "memberId"
                ],
                "properties": {
                  "memberId": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Capture requested",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/activities/summary": {
      "get": {
        "operationId": "getActivitySummary",
        "summary": "Get app usage summary",
        "description": "Returns top applications used, productivity score breakdown, and category classifications.",
        "tags": [
          "Activities"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Activity summary with app usage and productivity score",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/financials/summary": {
      "get": {
        "operationId": "getFinancialSummary",
        "summary": "Get profitability summary",
        "description": "Returns agency-wide financial summary with total revenue, costs, profit margins, and per-project breakdowns.",
        "tags": [
          "Financials"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Financial summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/financials/invoices": {
      "get": {
        "operationId": "listInvoices",
        "summary": "List invoices",
        "description": "Retrieve all invoices for the organization.",
        "tags": [
          "Financials"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of invoices",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createInvoice",
        "summary": "Create an invoice",
        "description": "Generate a new invoice from tracked hours or manual line items with Stripe payment processing.",
        "tags": [
          "Financials"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clientName"
                ],
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "clientName": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string",
                    "default": "USD"
                  },
                  "dueDate": {
                    "type": "string",
                    "format": "date"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "taxPercent": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "description",
                        "quantity",
                        "unitPrice"
                      ],
                      "properties": {
                        "description": {
                          "type": "string"
                        },
                        "quantity": {
                          "type": "number",
                          "minimum": 0
                        },
                        "unitPrice": {
                          "type": "number",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invoice created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/financials/invoices/{invoiceId}": {
      "get": {
        "operationId": "getInvoice",
        "summary": "Get invoice details",
        "description": "Retrieve a specific invoice with full line items and payment status.",
        "tags": [
          "Financials"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter invoiceId for getInvoice"
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/financials/invoices/{invoiceId}/status": {
      "patch": {
        "operationId": "updateInvoiceStatus",
        "summary": "Update invoice status",
        "description": "Change the status of an invoice (e.g., DRAFT, SENT, PAID, OVERDUE).",
        "tags": [
          "Financials"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter invoiceId for updateInvoiceStatus"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "DRAFT",
                      "SENT",
                      "PAID",
                      "OVERDUE",
                      "CANCELLED"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/ai/summary/daily": {
      "post": {
        "operationId": "getDailyAiSummary",
        "summary": "Generate AI daily summary",
        "description": "Uses AI to generate a daily standup summary of team activity, time tracked, and accomplishments. Requires AI access subscription.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "date"
                ],
                "properties": {
                  "date": {
                    "type": "string",
                    "format": "date",
                    "description": "Date to summarize (YYYY-MM-DD)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI-generated daily summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/ai/overview": {
      "get": {
        "operationId": "getAiOverview",
        "summary": "Gemini org overview",
        "description": "Google Gemini executive overview with project details, developer summaries, and working skills inferred from tracked apps. Requires Team plan AI access.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30,
              "default": 7
            },
            "description": "query parameter days for getAiOverview"
          }
        ],
        "responses": {
          "200": {
            "description": "Overview payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/ai/work-day": {
      "post": {
        "operationId": "reconstructAiWorkDay",
        "summary": "Reconstruct a work day",
        "description": "Rebuilds a task list for a teammate (or the whole team) on a date using time entries, app/window titles, and screenshot proof-of-work. Requires Team plan AI access.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "date"
                ],
                "properties": {
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "userId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "memberName": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Work day reconstruction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/ai/weekly-report": {
      "get": {
        "operationId": "getAiWeeklyReport",
        "summary": "Latest Friday weekly report",
        "description": "Returns the most recent Gemini weekly report for the organization.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Weekly report or null",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "generateAiWeeklyReport",
        "summary": "Generate weekly Gemini report",
        "description": "Creates a weekly workforce report (overview, projects, developers, skills) using Google Gemini.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Generated report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/ai/weekly-report/download": {
      "get": {
        "operationId": "downloadAiWeeklyReport",
        "summary": "Download weekly report markdown",
        "description": "Downloads the latest weekly report as a markdown file. Generates one if none exists.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown attachment or JSON envelope with markdown",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/ai/chat": {
      "post": {
        "operationId": "askAiChat",
        "summary": "Ask AI assistant",
        "description": "Chat with the AI assistant about team productivity, project status, and workforce insights. Requires AI access subscription.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "Question or request about team productivity"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/notifications": {
      "get": {
        "operationId": "listNotifications",
        "summary": "List notifications",
        "description": "Retrieve in-app notifications with optional unread filter.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "unreadOnly",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "query parameter unreadOnly for listNotifications"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "query parameter limit for listNotifications"
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "clearAllNotifications",
        "summary": "Clear all notifications",
        "description": "Delete all notifications for the current user.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications cleared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/notifications/{notificationId}/read": {
      "patch": {
        "operationId": "markNotificationRead",
        "summary": "Mark notification as read",
        "description": "Mark a single notification as read.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "parameters": [
          {
            "name": "notificationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "path parameter notificationId for markNotificationRead"
          }
        ],
        "responses": {
          "200": {
            "description": "Notification marked as read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/notifications/mark-all-read": {
      "post": {
        "operationId": "markAllNotificationsRead",
        "summary": "Mark all as read",
        "description": "Mark all notifications as read at once.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "All notifications marked as read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/rbac": {
      "get": {
        "operationId": "listRoles",
        "summary": "List roles and permissions",
        "description": "Returns all available roles with their permission sets.",
        "tags": [
          "RBAC"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Roles and permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    },
    "/billing/subscription-status": {
      "get": {
        "operationId": "getSubscriptionStatus",
        "summary": "Get subscription status",
        "description": "Returns current subscription plan, trial status, and feature access levels.",
        "tags": [
          "Financials"
        ],
        "security": [
          {
            "BearerAuth": [],
            "OrgHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT from POST /auth/login or /auth/register. Header: Authorization: Bearer <token>."
      },
      "OrgHeader": {
        "type": "apiKey",
        "name": "x-organization-id",
        "in": "header",
        "description": "UUID of the organization. Required for all multi-tenant endpoints."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request — validation error or missing required fields",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Unauthorized — missing or invalid authentication",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "required": [
          "success",
          "code",
          "error",
          "message",
          "hint"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "code": {
            "type": "string",
            "description": "Machine-readable error code",
            "example": "NOT_FOUND"
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message"
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message (alias of error)"
          },
          "hint": {
            "type": "string",
            "description": "How to resolve the error"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "SuccessResponse": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "object",
            "description": "Response payload"
          },
          "total": {
            "type": "integer",
            "description": "Total count for paginated responses"
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "timestamp",
          "service",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "healthy"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "service": {
            "type": "string",
            "example": "workforce-os-api"
          },
          "version": {
            "type": "string",
            "example": "1.0.0"
          }
        }
      }
    }
  }
}
