{"openapi":"3.0.0","paths":{"/account/info":{"get":{"description":"Returns information about the currently authenticated user within the current workspace.\n\n**What is returned:**\n- Basic profile information (name, email, avatar)\n- Workspace and role information\n- User type (Employee or Organization Member)\n- All granted permissions\n- Account preferences (language, email notifications, workspace color)\n- Identifiers for employee/member accounts\n\nThis endpoint is useful for determining the current user's identity, permissions, and account type before making other API calls.","operationId":"AccountController_info","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountInfoResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"User not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get current user information","tags":["account"],"x-required-scopes":["api:read"]}},"/account/employee/data":{"get":{"description":"Returns detailed employee profile information including personal data, contact information, and address.\n\n**What is returned:**\n- Personal information (name, email, phone, birth date)\n- Professional details (title, degree)\n- Complete address information\n- Digital signature file URL (if uploaded)\n\n**Note:** This endpoint is only available for employee accounts (not organization members). To check if a user is an employee, use GET /api/public/account/info and check the `type` field.","operationId":"AccountController_employeeData","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeDataResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee data not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get employee profile data","tags":["account","employee-only"],"x-required-scopes":["api:read"]},"post":{"description":"Partially updates employee profile information. All fields are optional - only provided fields will be updated.\n\n**What can be updated:**\n- Personal information (name, email, phone, birth date)\n- Professional details (title, degree)\n- Complete address information\n- Avatar image (upload file first via POST /api/public/workspace/upload, then use file ID)\n- Digital signature (upload file first via POST /api/public/workspace/upload, then use file ID)\n\n**Note:** This endpoint is only available for employee accounts. To upload files (avatar or signature), first call POST /api/public/workspace/upload to upload the file and get a file ID, then use that ID in the `avatarId` or `signatureFileId` field.","operationId":"AccountController_updateEmployeeData","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmployeeDataRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update employee profile data","tags":["account","employee-only"],"x-required-scopes":["api:write"]}},"/account/workspace-color":{"post":{"description":"Updates the user's workspace color theme preference. The workspace color affects the visual theme of the application interface for this user.","operationId":"AccountController_updateWorkspaceColor","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkspaceColorRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"User not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update workspace color preference","tags":["account"],"x-required-scopes":["api:write"]}},"/account/language":{"post":{"description":"Updates the user's preferred language for the application interface. Supported languages: `en` (English) or `de` (German). This affects the language of UI elements, notifications, and other system messages.","operationId":"AccountController_updateLanguage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLanguageRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"User not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update preferred language","tags":["account"],"x-required-scopes":["api:write"]}},"/account/email-notifications":{"post":{"description":"Enables or disables email notifications for the current user. When disabled, the user will not receive email notifications from the system, but will still see notifications within the application.","operationId":"AccountController_updateEmailNotifications","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailNotificationsRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"User not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update email notification preferences","tags":["account"],"x-required-scopes":["api:write"]}},"/account/employee/bank-data":{"get":{"description":"Returns employee bank account details including IBAN, BIC, bank name, and account owner name.\n\n**Note:** This endpoint is only available for employee accounts. Bank data is typically used for payroll processing.","operationId":"AccountController_getBankData","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankDataResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee bank data not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get employee bank account information","tags":["account","employee-only"],"x-required-scopes":["api:read"]},"post":{"description":"Updates employee bank account details. All fields are optional - only provided fields will be updated.\n\n**What can be updated:**\n- IBAN (International Bank Account Number)\n- BIC (Bank Identifier Code)\n- Bank name\n- Account owner name\n\n**Note:** This endpoint is only available for employee accounts. Bank data is typically used for payroll processing.","operationId":"AccountController_updateBankData","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBankDataRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update employee bank account information","tags":["account","employee-only"],"x-required-scopes":["api:write"]}},"/account/org-member/data":{"get":{"description":"Returns detailed organization member profile information including personal data, contact information, and address.\n\n**What is returned:**\n- Personal information (name, email, phone, birth date)\n- Professional details (title, degree, position)\n- Complete address information\n\n**Note:** This endpoint is only available for organization member accounts (not employees). Organization members are external contacts (e.g., clients, partners) who have access to the workspace.","operationId":"AccountController_getOrgMemberData","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMemberDataResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Organization member data not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization member profile data","tags":["account","org-member-only"],"x-required-scopes":["api:read"]},"post":{"description":"Partially updates organization member profile information. All fields are optional - only provided fields will be updated.\n\n**What can be updated:**\n- Personal information (name, email, phone, birth date)\n- Professional details (title, degree, position)\n- Complete address information\n- Avatar image (upload file first via POST /api/public/workspace/upload, then use file ID)\n\n**Note:** This endpoint is only available for organization member accounts. Organization members are external contacts (e.g., clients, partners) who have access to the workspace. To upload an avatar, first call POST /api/public/workspace/upload to upload the file and get a file ID, then use that ID in the `avatarId` field.","operationId":"AccountController_updateOrgMemberData","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrgMemberDataRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Organization member not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization member profile data","tags":["account","org-member-only"],"x-required-scopes":["api:write"]}},"/account/employee/vacations/future":{"get":{"description":"Returns all future vacation requests for the current employee.\n\n**What is returned:**\n- Vacation request ID\n- Start and end dates\n- Current status (Pending, Approved, or Rejected)\n\n**Note:** This endpoint is only available for employee accounts. Future vacations are those with a start date in the future.","operationId":"AccountController_getFutureVacations","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FutureVacationResponse"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get future vacation requests","tags":["account","employee-only","vacations"],"x-required-scopes":["api:read"]}},"/account/employee/vacations/stats":{"get":{"description":"Returns comprehensive vacation statistics for the current employee for a specific year.\n\n**What is returned:**\n- Vacation entitlement by contract\n- Days periods (when vacation days were added/deducted)\n- Pending vacation days (requests awaiting approval)\n- Approved future vacation days\n- Approved past vacation days (already taken)\n- Vacation days from overtime conversion\n- Added/deducted days (manual adjustments)\n- Total vacation days available\n- Total used vacation days\n- Total remaining vacation days\n- Potential vacation days (including pending requests)\n\n**Note:** This endpoint is only available for employee accounts. If no year is provided in the query parameter, the current year is used.","operationId":"AccountController_getVacationStats","parameters":[{"name":"year","required":false,"in":"query","description":"Year for vacation stats","schema":{"example":2024,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VacationStatsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get vacation statistics","tags":["account","employee-only","vacations"],"x-required-scopes":["api:read"]}},"/account/employee/vacations/history":{"get":{"description":"Returns a paginated list of all vacation requests for the current employee.\n\n**What is returned:**\n- Complete history of all vacation requests (past and future)\n- Request dates, status, and approval information\n- Reason for vacation (if provided)\n- Who approved/rejected and when\n\n**Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.","operationId":"AccountController_getVacationHistory","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Vacation request ID\n- **dateFrom** (date): Vacation start date\n- **dateTo** (date): Vacation end date\n- **status** (set): Vacation request status\n- **employeeId** (string): Employee ID\n- **statusChangedAt** (date): Status change timestamp\n- **statusChangedBy** (string): User ID who changed status\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Vacation request ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **dateFrom** (date): Vacation start date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **dateTo** (date): Vacation end date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **status** (set): Vacation request status (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **statusChangedAt** (date): Status change timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **statusChangedBy** (string): User ID who changed status (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Vacation request ID\n- **dateFrom**: Vacation start date\n- **dateTo**: Vacation end date\n- **status**: Vacation request status\n- **employeeId**: Employee ID\n- **statusChangedAt**: Status change timestamp\n- **statusChangedBy**: User ID who changed status\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** dateFrom (desc)","schema":{"example":"[{\"field\":\"dateFrom\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, dateFrom, dateTo, status, employeeId, statusChangedAt, statusChangedBy, reason","schema":{"example":"id,dateFrom","type":"array","items":{}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VacationHistoryGridResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get vacation request history","tags":["account","employee-only","vacations"],"x-required-scopes":["api:read"]}},"/account/employee/vacations/compensation":{"get":{"description":"Returns a paginated list of all vacation compensation entries for the current employee.\n\n**What is vacation compensation?**\nVacation compensation includes:\n- Unused vacation days carried over to the next year\n- Vacation days paid out instead of taken\n- Vacation days converted from overtime hours\n- Manual adjustments (added or deducted days)\n\n**What is returned:**\n- Compensation type\n- Year and target year (for carryovers)\n- Number of days\n- Creation date\n\n**Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.","operationId":"AccountController_getVacationCompensation","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Compensation entry ID\n- **employeeId** (string): Employee ID\n- **type** (set): Compensation type\n- **year** (number): Compensation year\n- **toYear** (number): Target year for compensation\n- **days** (number): Number of compensation days\n- **createdAt** (date): Creation timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Compensation entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Compensation type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **year** (number): Compensation year (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **toYear** (number): Target year for compensation (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **days** (number): Number of compensation days (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Compensation entry ID\n- **employeeId**: Employee ID\n- **type**: Compensation type\n- **year**: Compensation year\n- **toYear**: Target year for compensation\n- **days**: Number of compensation days\n- **createdAt**: Creation timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, employeeId, type, year, toYear, days, createdAt","schema":{"example":"id,employeeId","type":"array","items":{}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VacationCompensationGridResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get vacation compensation history","tags":["account","employee-only","vacations"],"x-required-scopes":["api:read"]}},"/account/employee/vacations/request":{"post":{"description":"Creates a new vacation request for the current employee.\n\n**How vacation requests work:**\n1. Employee submits a vacation request with start and end dates\n2. Request status is set to \"Pending\"\n3. Manager reviews and approves or rejects the request\n4. System automatically checks for scheduling conflicts\n\n**Validation rules:**\n- Start date cannot be more than one month in the past\n- End date must be after start date\n- System checks for overlapping events (other vacations, sick days, etc.)\n\n**Note:** This endpoint is only available for employee accounts. Only pending vacations can be modified or canceled.","operationId":"AccountController_requestVacation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestVacationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"dateFrom":{"type":"array","items":{"type":"string"},"example":["Vacation start date cannot be more than one month in the past."]},"dateTo":{"type":"array","items":{"type":"string"},"example":["Vacation end date must be after start date."]},"globalErrors":{"type":"array","items":{"type":"string"},"example":["There are already other events for this period. Please select another one."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Request vacation","tags":["account","employee-only","vacations"],"x-required-scopes":["api:write"]}},"/account/employee/sick-days":{"get":{"description":"Returns a paginated list of all sick day records for the current employee.\n\n**What is returned:**\n- Start and end dates of sick periods\n- Comments/notes about the illness\n- Attached medical certificate file IDs (if provided)\n\n**Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.","operationId":"AccountController_getSickDays","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Sickness entry ID\n- **dateFrom** (date): Sick leave start date\n- **dateTo** (date): Sick leave end date\n- **employeeId** (string): Employee ID\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Sickness entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **dateFrom** (date): Sick leave start date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **dateTo** (date): Sick leave end date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Sickness entry ID\n- **dateFrom**: Sick leave start date\n- **dateTo**: Sick leave end date\n- **employeeId**: Employee ID\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** dateFrom (desc)","schema":{"example":"[{\"field\":\"dateFrom\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, dateFrom, dateTo, employeeId, comment, attachedFilesIds","schema":{"example":"id,dateFrom","type":"array","items":{}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SicknessGridResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Employee not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get sick days history","tags":["account","employee-only","sick-days"],"x-required-scopes":["api:read"]}},"/account/employee/sick-days/request":{"post":{"description":"Creates a new sick days record for the current employee.\n\n**How sick days work:**\n1. Employee reports sick days with start and end dates\n2. Optional comment can be added\n3. Medical certificates can be attached (upload files first, then provide file IDs)\n4. System automatically checks for overlapping events\n\n**To upload medical certificates:**\n1. Call POST /api/public/workspace/upload for each file\n2. Get the file ID from the response\n3. Include the file IDs in the `attachedFilesIds` array\n\n**Note:** This endpoint is only available for employee accounts. The system validates that there are no overlapping events (vacations, other sick days, etc.) for the specified period.","operationId":"AccountController_requestSickness","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestSicknessDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"dateFrom":{"type":"array","items":{"type":"string"},"example":["Date from is required."]},"dateTo":{"type":"array","items":{"type":"string"},"example":["Date to is required."]},"globalErrors":{"type":"array","items":{"type":"string"},"example":["There are already other events for this period. Please select another one."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Request sick days","tags":["account","employee-only","sick-days"],"x-required-scopes":["api:write"]}},"/account/journal":{"get":{"description":"Returns a paginated list of journal entries for the current user.\n\n**What is a Journal?**\nThe journal is a personal note-taking system where users can document:\n- Personal notes and observations\n- Feedback and conversations\n- Goal agreements and reminders\n- Mood tracking (Happy, Neutral, Sad)\n\n**What is returned:**\n- Entry content (formatted as HTML)\n- Creation date and creator\n- Mood indicator\n- Reminder date (if set)\n\n**Note:** Available for both employees and organization members. Supports server-side pagination, sorting, and filtering via grid query parameters.","operationId":"AccountController_getJournal","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Journal entry ID\n- **createdAt** (date): Journal entry creation timestamp\n- **createdBy** (string): Creator user ID\n- **mood** (set): Journal entry mood\n- **reminder** (date): Reminder date\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Journal entry ID\n- **createdAt**: Journal entry creation timestamp\n- **createdBy**: Creator user ID\n- **mood**: Journal entry mood\n- **reminder**: Reminder date\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, createdAt, createdBy, mood, reminder, body","schema":{"example":"id,createdAt","type":"array","items":{}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalGridResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get journal entries","tags":["account","journal"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new journal entry for the current user.\n\n**What is a Journal Entry?**\nA journal entry is a personal note that can include:\n- Formatted text content (HTML or Markdown)\n- Mood indicator (Happy 😊, Neutral 😐, or Sad 😢)\n- Optional reminder date for follow-up\n\n**Use cases:**\n- Document personal notes and observations\n- Track feedback and conversations\n- Set reminders for goal reviews\n- Mood tracking over time\n\n**Note:** Available for both employees and organization members. The body field accepts HTML or Markdown input and is stored internally in IDoc format.","operationId":"AccountController_createJournal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"body":{"type":"array","items":{"type":"string"},"example":["Body is required."]},"reminder":{"type":"array","items":{"type":"string"},"example":["Reminder must be a valid ISO8601 date."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create journal entry","tags":["account","journal"],"x-required-scopes":["api:write"]},"put":{"description":"Updates an existing journal entry. All fields can be updated.\n\n**What can be updated:**\n- Entry content (body)\n- Mood indicator\n- Reminder date\n\n**Note:** You can only update your own journal entries. The body field accepts HTML or Markdown input and is stored internally in IDoc format.","operationId":"AccountController_updateJournal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"id":{"type":"array","items":{"type":"string"},"example":["ID is required for updates."]},"body":{"type":"array","items":{"type":"string"},"example":["Body is required."]},"reminder":{"type":"array","items":{"type":"string"},"example":["Reminder must be a valid ISO8601 date."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update journal entry","tags":["account","journal"],"x-required-scopes":["api:write"]}},"/account/journal/{id}":{"patch":{"description":"Updates only the provided fields of an existing journal entry. Fields not provided will remain unchanged.\n\n**What can be updated:**\n- Entry content (body) - optional\n- Mood indicator - optional\n- Reminder date - optional\n\n**Note:** You can only update your own journal entries. The body field accepts HTML or Markdown input and is stored internally in IDoc format.","operationId":"AccountController_patchJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"body":{"type":"array","items":{"type":"string"},"example":["Body is required."]},"reminder":{"type":"array","items":{"type":"string"},"example":["Reminder must be a valid ISO8601 date."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update journal entry","tags":["account","journal"],"x-required-scopes":["api:write"]},"delete":{"description":"Permanently deletes a journal entry.\n\n**Note:** You can only delete your own journal entries. This action cannot be undone.","operationId":"AccountController_deleteJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"id":{"type":"array","items":{"type":"string"},"example":["Journal entry not found."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete journal entry","tags":["account","journal"],"x-required-scopes":["api:write"]}},"/account/employee/vacations/{id}":{"put":{"description":"Updates an existing vacation request. Only pending vacation requests can be updated.\n\n**What can be updated:**\n- Start date\n- End date\n\n**Validation rules:**\n- Start date cannot be more than one month in the past\n- End date must be after start date\n- System checks for overlapping events\n\n**Note:** This endpoint is only available for employee accounts. Once a vacation is approved or rejected, it cannot be updated. To cancel an approved vacation, use DELETE /api/public/account/employee/vacations/:id.","operationId":"AccountController_updateVacation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVacationRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"dateFrom":{"type":"array","items":{"type":"string"},"example":["Vacation start date cannot be more than one month in the past."]},"dateTo":{"type":"array","items":{"type":"string"},"example":["Vacation end date must be after start date."]},"globalErrors":{"type":"array","items":{"type":"string"},"example":["There are already other events for this period. Please select another one."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update vacation request","tags":["account","employee-only","vacations"],"x-required-scopes":["api:write"]},"delete":{"description":"Cancels an existing vacation request.\n\n**When can vacations be canceled:**\n- Pending vacation requests (awaiting approval)\n- Future approved vacations (not yet started)\n\n**Note:** This endpoint is only available for employee accounts. Past vacations or vacations that have already started cannot be canceled.","operationId":"AccountController_cancelVacation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Vacation not found or cannot be canceled"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Cancel vacation request","tags":["account","employee-only","vacations"],"x-required-scopes":["api:write"]}},"/account/employee/sick-days/{id}":{"put":{"description":"Updates an existing sick days record.\n\n**What can be updated:**\n- Start and end dates\n- Comment/notes\n- Attached medical certificate file IDs\n\n**To update medical certificates:**\n1. Upload new files via POST /api/public/workspace/upload\n2. Get file IDs from responses\n3. Include all file IDs (old and new) in `attachedFilesIds` array\n\n**Note:** This endpoint is only available for employee accounts. The system validates that there are no overlapping events for the specified period.","operationId":"AccountController_updateSickness","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSicknessRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"dateFrom":{"type":"array","items":{"type":"string"},"example":["Date from is required."]},"dateTo":{"type":"array","items":{"type":"string"},"example":["Date to is required."]},"attachedFilesIds":{"type":"array","items":{"type":"string"},"example":["Attached files IDs are required."]},"globalErrors":{"type":"array","items":{"type":"string"},"example":["There are already other events for this period. Please select another one."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update sick days record","tags":["account","employee-only","sick-days"],"x-required-scopes":["api:write"]},"delete":{"description":"Permanently deletes a sick days record.\n\n**Note:** This endpoint is only available for employee accounts. This action cannot be undone. Use with caution.","operationId":"AccountController_deleteSickness","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Sick days record not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete sick days record","tags":["account","employee-only","sick-days"],"x-required-scopes":["api:write"]}},"/account/overtime/stats":{"get":{"description":"Returns comprehensive overtime statistics for the current employee.\n\n**What is Overtime?**\nOvertime is automatically calculated from daily time tracking. If an employee works more hours than their contract specifies, positive overtime is created. If they work fewer hours, the balance goes negative.\n\n**What is returned:**\n- **Overtime balance**: Current balance of all plus or minus hours\n- **Total recorded**: Total sum of all logged working hours\n- **Contract**: Weekly or daily target hours according to the contract\n- **Total compensated**: Sum of hours that have already been balanced out\n- **Converted to vacation**: Hours that were turned into extra vacation days\n- **Paid in cash**: Overtime hours that were paid out\n- **Ignored**: Overtime marked as expired or without compensation\n\n**Note:** This endpoint is only available for employee accounts.","operationId":"AccountController_getOvertimeStats","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OvertimeStatsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get overtime statistics","tags":["account","employee-only","overtime"],"x-required-scopes":["api:read"]}},"/account/overtime/compensation-history":{"get":{"description":"Returns a paginated list of all overtime compensation entries for the current employee.\n\n**What is Overtime Compensation?**\nOvertime can be compensated in three ways:\n- **Paid in cash**: Overtime is paid out\n- **Turned into vacation days**: Overtime is swapped for days off\n- **Ignored**: Overtime is removed without compensation\n\n**What is returned:**\n- Compensation type\n- Date of compensation\n- Number of hours compensated\n- Related information (e.g., target year for vacation conversions)\n\n**Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.","operationId":"AccountController_getOvertimeCompensationHistory","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Overtime compensation ID\n- **employeeId** (string): Employee ID\n- **type** (set): Compensation type\n- **year** (number): Compensation year\n- **toYear** (number): Target year\n- **hours** (number): Number of overtime hours\n- **createdAt** (date): Creation timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Overtime compensation ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Compensation type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **year** (number): Compensation year (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **toYear** (number): Target year (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **hours** (number): Number of overtime hours (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Overtime compensation ID\n- **employeeId**: Employee ID\n- **type**: Compensation type\n- **year**: Compensation year\n- **toYear**: Target year\n- **hours**: Number of overtime hours\n- **createdAt**: Creation timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, employeeId, type, year, toYear, hours, createdAt","schema":{"example":"id,employeeId","type":"array","items":{}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OvertimeCompensationGridResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get overtime compensation history","tags":["account","employee-only","overtime"],"x-required-scopes":["api:read"]}},"/account/notifications":{"get":{"description":"Returns a paginated list of notifications for the current user.\n\n**What are Notifications?**\nNotifications inform users about important events in the system:\n- Project updates\n- Task assignments\n- Vacation request approvals/rejections\n- Comments and mentions\n- System announcements\n- And more...\n\n**What is returned:**\n- Notification type and entity information\n- Human-readable message\n- Read/unread status\n- Creation date\n- Creator information\n\n**Query parameters:**\n- `unreadOnly`: Set to `true` to return only unread notifications\n\n**Note:** Supports server-side pagination, sorting, and filtering via grid query parameters.","operationId":"AccountController_getNotifications","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Notification ID\n- **type** (set): Notification type\n- **entity** (string): Related entity type\n- **entityId** (string): Related entity ID\n- **creatorId** (string): Creator user ID\n- **isRead** (boolean): Read status\n- **createdAt** (date): Creation timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Notification ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Notification type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **entity** (string): Related entity type (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **entityId** (string): Related entity ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **creatorId** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **isRead** (boolean): Read status (boolean filter). Available comparisons: equal, not_equal\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Notification ID\n- **type**: Notification type\n- **entity**: Related entity type\n- **entityId**: Related entity ID\n- **creatorId**: Creator user ID\n- **isRead**: Read status\n- **createdAt**: Creation timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, type, entity, entityId, creatorId, isRead, createdAt, message","schema":{"example":"id,type","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated notifications with human-readable messages","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"entity":{"type":"string"},"entityId":{"type":"string"},"creatorId":{"type":"string"},"isRead":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"message":{"type":"string","description":"Human-readable message"}}}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}}}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"unreadOnly":{"type":"array","items":{"type":"string"},"example":["unreadOnly must be a boolean value"]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get user notifications","tags":["account","notifications"],"x-required-scopes":["api:read"]}},"/account/notifications/{id}/read":{"post":{"description":"Marks a specific notification as read for the current user.\n\n**What happens:**\n- The notification's `isRead` status is set to `true`\n- The notification will no longer appear in unread-only queries\n- The read status persists across sessions\n\n**Note:** You can only mark your own notifications as read.","operationId":"AccountController_markNotificationAsRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"id":{"type":"array","items":{"type":"string"},"example":["Notification not found."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark notification as read","tags":["account","notifications"],"x-required-scopes":["api:write"]}},"/account/notifications/read-all":{"post":{"description":"Marks all unread notifications as read for the current user.\n\n**What happens:**\n- All unread notifications for the current user are marked as read\n- Returns the count of notifications that were marked as read\n- Useful for bulk \"mark all as read\" functionality\n\n**Response includes:**\n- `success`: Boolean indicating operation success\n- `count`: Number of notifications marked as read","operationId":"AccountController_markAllNotificationsAsRead","parameters":[],"responses":{"200":{"description":"Number of notifications marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"count":{"type":"number","example":5,"description":"Number of notifications marked as read"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark all notifications as read","tags":["account","notifications"],"x-required-scopes":["api:write"]}},"/agent-connectors/setup/claim":{"post":{"description":"Used by external agent runtimes such as OpenClaw to finish bot connector setup. The setup token is generated in the authenticated Leadtime app, but this claim endpoint does not require Bearer auth. Claiming the token enables the bot webhooks/sessions connection, creates a fresh bot PAT, stores the webhook URL, and returns one-time secrets to the installer.","operationId":"AgentConnectorsController_claim","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimAgentConnectorSetupDto"}}}},"responses":{"201":{"description":"Connector setup was claimed and one-time secrets were returned."},"401":{"description":"Setup token is invalid, expired, or already used."},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"summary":"Claim a one-time bot connector setup token","tags":["agent-connectors"]}},"/agent-sessions/{runId}/context":{"get":{"operationId":"AgentSessionsController_getContext","parameters":[{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get agent session context for a self-hosted wrapper","tags":["agent-sessions"],"x-required-scopes":["api:read"]}},"/agent-sessions/{runId}/activities":{"post":{"operationId":"AgentSessionsController_appendActivity","parameters":[{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendAgentSessionActivityDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Append activity to an agent session","tags":["agent-sessions"],"x-required-scopes":["api:write"]},"get":{"operationId":"AgentSessionsController_listActivities","parameters":[{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List agent session activities","tags":["agent-sessions"],"x-required-scopes":["api:read"]}},"/agent-sessions/{runId}/status":{"patch":{"operationId":"AgentSessionsController_updateStatus","parameters":[{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentSessionStatusDto"}}}},"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update agent session status","tags":["agent-sessions"],"x-required-scopes":["api:write"]}},"/agent-sessions/{runId}":{"get":{"operationId":"AgentSessionsController_getSession","parameters":[{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get an agent session","tags":["agent-sessions"],"x-required-scopes":["api:read"]}},"/agent-sessions/{runId}/webhook-deliveries":{"get":{"operationId":"AgentSessionsController_listWebhookDeliveries","parameters":[{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List agent session webhook deliveries","tags":["agent-sessions"],"x-required-scopes":["api:read"]}},"/account/favorites/toggle":{"post":{"description":"Toggles a favorite for a specific entity (task, project, organization, or command). If the entity is currently favorited, it will be removed from favorites. If it is not favorited, it will be added to favorites.\n\n**Entity Types:**\n- `Project`: Favorite a project\n- `Task`: Favorite a task\n- `Organization`: Favorite an organization\n- `Command`: Favorite a command (for command palette)\n\nFavorites are user-specific and workspace-scoped. Each user maintains their own list of favorites. When adding a new favorite, it is automatically appended to the end of the favorites list. The response indicates whether the entity is now favorited (`isFavorite: true`) or not (`isFavorite: false`).","operationId":"FavoritesController_toggle","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleFavoriteRequestDto"}}}},"responses":{"200":{"description":"Favorite status toggled successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleFavoriteResponseDto"}}}},"400":{"description":"Invalid entity ID or entity type"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Toggle favorite on/off","tags":["account","favorites"],"x-required-scopes":["api:write"]}},"/account/favorites":{"get":{"description":"Returns all favorites for the authenticated user, sorted by custom sort order (if set) or creation date. Each favorite includes entity-specific metadata:\n\n**Task favorites:** Include `shortNumber` and `title`\n**Project favorites:** Include `shortNumber`, `shortName` (e.g., \"ORG-123\"), and `name`\n**Organization favorites:** Include `shortName` and `name`\n**Command favorites:** Include `id` and `name`\n\nDeleted entities are automatically excluded from the results. The favorites list is user-specific and workspace-scoped. Use the toggle endpoint to add or remove favorites.","operationId":"FavoritesController_list","parameters":[],"responses":{"200":{"description":"Favorites list retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoritesListResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get list of favorites","tags":["account","favorites"],"x-required-scopes":["api:read"]}},"/administration/form-templates-available":{"get":{"description":"Same data as GET `/workspace/form-templates`: categories plus template list items (including each template `id`). Use a template `id` as `templateId` in POST `/tasks/{identifier}/form-instances`. Prefer GET `/workspace/form-templates` for new integrations; field schemas are available from GET `/workspace/form-templates/{id}`.","operationId":"FormTemplatesController_getFormTemplatesAvailable","parameters":[],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List form categories and templates (read-only, for attaching forms to tasks)","tags":["administration","form-templates"],"x-required-scopes":["api:read"]}},"/administration/form-categories":{"get":{"operationId":"FormTemplatesController_getFormCategories","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FormCategoryResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List form categories","tags":["administration","form-templates"],"x-required-scopes":["api:read"]},"post":{"operationId":"FormTemplatesController_createFormCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFormCategoryDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create form category","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-categories/{id}":{"put":{"operationId":"FormTemplatesController_updateFormCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFormCategoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update form category","tags":["administration","form-templates"],"x-required-scopes":["api:write"]},"delete":{"operationId":"FormTemplatesController_deleteFormCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete form category","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-categories/sort":{"post":{"operationId":"FormTemplatesController_sortFormCategories","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortFormCategoriesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder form categories","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-templates/sort":{"post":{"operationId":"FormTemplatesController_sortFormTemplates","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortFormTemplatesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder form templates","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-templates/{templateId}/fields":{"get":{"operationId":"FormTemplatesController_getFormTemplateFields","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FormTemplateFieldResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List fields for a form template","tags":["administration","form-templates"],"x-required-scopes":["api:read"]},"post":{"operationId":"FormTemplatesController_createFormTemplateField","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFormTemplateFieldDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create form template field","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-templates/{templateId}/fields/sort":{"post":{"operationId":"FormTemplatesController_sortFormTemplateFields","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortFormTemplateFieldsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder fields on a form template","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-templates/{templateId}/fields/{fieldId}":{"put":{"operationId":"FormTemplatesController_updateFormTemplateField","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}},{"name":"fieldId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFormTemplateFieldDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update form template field","tags":["administration","form-templates"],"x-required-scopes":["api:write"]},"delete":{"operationId":"FormTemplatesController_deleteFormTemplateField","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}},{"name":"fieldId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete form template field","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-templates":{"get":{"operationId":"FormTemplatesController_getFormTemplates","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FormTemplateListItemResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List form templates","tags":["administration","form-templates"],"x-required-scopes":["api:read"]},"post":{"operationId":"FormTemplatesController_createFormTemplate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFormTemplateDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create form template","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/administration/form-templates/{id}":{"get":{"operationId":"FormTemplatesController_getFormTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormTemplateDetailResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get form template with fields","tags":["administration","form-templates"],"x-required-scopes":["api:read"]},"put":{"operationId":"FormTemplatesController_updateFormTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFormTemplateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update form template","tags":["administration","form-templates"],"x-required-scopes":["api:write"]},"delete":{"operationId":"FormTemplatesController_deleteFormTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete form template","tags":["administration","form-templates"],"x-required-scopes":["api:write"]}},"/attendance":{"post":{"description":"Creates a new attendance record or updates an existing one for the current employee.\n\n**What is Attendance Tracking?**\nAttendance tracking records when employees clock in and clock out (check in/check out). It captures the start and end times of work periods, allowing the system to:\n- Calculate total hours worked\n- Track presence and absence\n- Monitor team availability\n- Integrate with time tracking and overtime calculations\n\n**What can be recorded:**\n- **Date**: The work date\n- **Start time** (timeFrom): When the employee clocked in (required, format: HH:mm)\n- **End time** (timeTo): When the employee clocked out (optional, format: HH:mm)\n- **Mood**: Employee mood for the day (Good, Neutral, Bad)\n- **Comment**: Optional notes about the workday\n\n**How it works:**\n- If an attendance record already exists for the date, it will be updated\n- If no record exists, a new one will be created\n- Total hours are automatically calculated from start and end times\n- The system validates that attendance doesn't overlap with vacation or sick days\n\n**Note:** This endpoint is only available for employee accounts. Attendance tracking must be enabled in the workspace settings.","operationId":"AttendanceController_createOrUpdate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttendanceRequestDto"}}}},"responses":{"200":{"description":"Attendance record created or updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttendanceResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"date":{"type":"array","items":{"type":"string"},"example":["Date is required."]},"timeFrom":{"type":"array","items":{"type":"string"},"example":["Time from is required."]},"mood":{"type":"array","items":{"type":"string"},"example":["Mood is required."]},"globalErrors":{"type":"array","items":{"type":"string"},"example":["Attendance overlaps with vacation or sickness period."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Not an employee or attendance not enabled in workspace","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":403},"message":{"type":"string","example":"Forbidden"}}}}}}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update attendance record","tags":["attendance","employee-only"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes an attendance record for a specific date.\n\n**What happens:**\n- The attendance record for the specified date is permanently deleted\n- This action cannot be undone\n- Deleted records are marked with a deletion timestamp\n\n**Note:** This endpoint is only available for employee accounts. Attendance tracking must be enabled in the workspace settings. You can only delete your own attendance records.","operationId":"AttendanceController__delete","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAttendanceRequestDto"}}}},"responses":{"200":{"description":"Attendance record deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","properties":{"date":{"type":"array","items":{"type":"string"},"example":["Date is required."]}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Not an employee or attendance not enabled in workspace","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":403},"message":{"type":"string","example":"Forbidden"}}}}}}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete attendance record","tags":["attendance","employee-only"],"x-required-scopes":["api:write"]}},"/bots/provider-api-keys":{"get":{"operationId":"BotsController_listProviderApiKeys","parameters":[{"name":"provider","required":true,"in":"query","schema":{"type":"string","enum":["cursorCloud","claudeManaged"]}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List saved hosted-agent provider API keys","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"operationId":"BotsController_createProviderApiKey","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkspaceProviderApiKeyDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a saved hosted-agent provider API key","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/provider-api-keys/{id}":{"delete":{"operationId":"BotsController_deleteProviderApiKey","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a saved hosted-agent provider API key","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/claude/skills":{"get":{"operationId":"BotsController_listClaudeWorkspaceSkills","parameters":[],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List workspace custom Claude skills","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"operationId":"BotsController_createClaudeWorkspaceSkill","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkspaceAgentSkillDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a workspace custom Claude skill","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/claude/skills/{skillId}":{"patch":{"operationId":"BotsController_updateClaudeWorkspaceSkill","parameters":[{"name":"skillId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update a workspace custom Claude skill","tags":["bots"],"x-required-scopes":["api:write"]},"delete":{"operationId":"BotsController_archiveClaudeWorkspaceSkill","parameters":[{"name":"skillId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Archive a workspace custom Claude skill","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots":{"get":{"description":"Lists bot users in this workspace, including their enabled state and hosted-agent provider key when configured.","operationId":"BotsController_list","parameters":[],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List bot users","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a bot user. Set connectionProvider to selfHosted, cursorCloud, or claudeManaged to initialize hosted-agent settings.","operationId":"BotsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBotDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a bot user","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}":{"get":{"operationId":"BotsController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get bot user details","tags":["bots"],"x-required-scopes":["api:read"]},"patch":{"operationId":"BotsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update a bot user","tags":["bots"],"x-required-scopes":["api:write"]},"delete":{"operationId":"BotsController__delete","parameters":[],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a bot user","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/status":{"patch":{"operationId":"BotsController_setStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPublicBotStatusDto"}}}},"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Enable or disable a bot user","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/connection-provider":{"post":{"operationId":"BotsController_setConnectionProvider","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPublicBotConnectionProviderDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Set bot hosted-agent connection provider","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/agent-settings":{"get":{"description":"Returns self-hosted, Cursor Cloud, or Claude Managed settings for the bot depending on its selected connection provider.","operationId":"BotsController_getAgentSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get selected bot agent settings","tags":["bots"],"x-required-scopes":["api:read"]}},"/bots/{id}/self-hosted":{"get":{"operationId":"BotsController_getSelfHosted","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get self-hosted bot agent settings","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"operationId":"BotsController_saveSelfHosted","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicBotAgentSettingsDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Save self-hosted bot agent settings","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/self-hosted/enabled":{"post":{"operationId":"BotsController_setSelfHostedEnabled","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPublicBotConnectionEnabledDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Enable or disable the self-hosted bot connection","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/self-hosted/webhook-secret/rotate":{"post":{"operationId":"BotsController_rotateSelfHostedWebhookSecret","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Rotate the self-hosted bot webhook secret","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/connector-setup-token":{"post":{"operationId":"BotsController_createConnectorSetupToken","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a one-time connector setup token for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/pat-tokens":{"get":{"operationId":"BotsController_listPatTokens","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List personal access tokens for a bot","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"operationId":"BotsController_createPatToken","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a personal access token for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/pat-tokens/{tokenId}":{"delete":{"operationId":"BotsController_revokePatToken","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"tokenId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Revoke a personal access token for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/cursor":{"get":{"operationId":"BotsController_getCursor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get Cursor Cloud settings for a bot","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"operationId":"BotsController_saveCursor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavePublicCursorCloudBotSettingsDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Save Cursor Cloud settings for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/cursor/validate":{"post":{"operationId":"BotsController_validateCursor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatePublicCursorCloudConnectionDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Validate Cursor Cloud credentials","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/cursor/repositories/refresh":{"post":{"operationId":"BotsController_refreshCursorRepositories","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatePublicCursorCloudConnectionDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Refresh Cursor Cloud repository choices for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/cursor/models/refresh":{"post":{"operationId":"BotsController_refreshCursorModels","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatePublicCursorCloudConnectionDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Refresh Cursor Cloud model choices for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude":{"get":{"description":"Returns the selected provider API key, connection status, Leadtime-managed agent configuration, model cache, custom MCP servers, and GitHub repository defaults. Agents should read this before deciding whether the bot is empty or asking what to improve.","operationId":"BotsController_getClaude","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get Claude Managed Agent settings for a bot","tags":["bots"],"x-required-scopes":["api:read"]},"post":{"description":"Saves Claude Managed settings. Prefer providerApiKeyId from /bots/provider-api-keys when a saved Anthropic key exists. Put Leadtime-managed model, editor-field instructions, and custom MCP servers under config.managedAgent. Instructions are a standard Leadtime editor field: send HTML, Markdown, or ProseMirror IDoc JSON, and Leadtime converts it to internal IDoc before syncing HTML to Claude. Put GitHub repository defaults under config.githubRepositoryDefaults. Use config.toolMode=basic by default when the bot only needs task context, comments, and status updates while doing external work or analysis. Use config.toolMode=full only when the bot is meant to perform broad Leadtime workspace actions such as creating or editing projects, tasks, organizations, contacts, forms, planning data, settings, or other company-management records. Re-read this endpoint after saving to verify the selected model, tool mode, MCP servers, and repositories.","operationId":"BotsController_saveClaude","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavePublicClaudeManagedBotSettingsDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Save Claude Managed Agent settings for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/validate":{"post":{"operationId":"BotsController_validateClaude","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatePublicClaudeManagedConnectionDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Validate Claude Managed Agent credentials","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/resources/refresh":{"post":{"description":"Refreshes Claude resource metadata with the configured Anthropic API key. Use this after selecting or adding an API key when model choices are missing, then save config.managedAgent.modelId using one of the returned model ids.","operationId":"BotsController_refreshClaudeResources","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatePublicClaudeManagedConnectionDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Refresh Claude agents, environments, vaults, and models for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/models/refresh":{"post":{"description":"Lists Claude models using the bot configured Anthropic key, or an optional providerApiKeyId/anthropicApiKey in the request body. When using a saved key, the bot model cache is updated so the UI and agents can select config.managedAgent.modelId from the same list.","operationId":"BotsController_refreshClaudeModels","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatePublicClaudeManagedConnectionDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Refresh Claude model choices for a bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/skills/catalog":{"post":{"description":"Returns Anthropic built-in skills plus workspace custom skills. Pass providerApiKeyId when you want to refresh Anthropic catalog using a specific saved key.","operationId":"BotsController_listClaudeSkillCatalog","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"List Anthropic and workspace Claude skills for bot configuration","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/github/install-url":{"post":{"operationId":"BotsController_getClaudeGithubInstallUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a GitHub App installation URL for Claude Managed GitHub access","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/github/repositories/refresh":{"post":{"operationId":"BotsController_refreshClaudeGithubRepositories","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Refresh GitHub repositories available to a Claude Managed bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/github/disconnect":{"post":{"operationId":"BotsController_disconnectClaudeGithub","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Disconnect GitHub from a Claude Managed bot","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/{id}/claude/managed-mcp/oauth-connect-url":{"post":{"operationId":"BotsController_getClaudeManagedMcpOAuthConnectUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicClaudeManagedMcpOAuthConnectUrlDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create an OAuth connect URL for a Claude Managed custom MCP server","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/project-agent-provider-settings/{projectId}":{"get":{"operationId":"BotsController_listProjectAgentProviderSettings","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project-level hosted-agent overrides","tags":["bots"],"x-required-scopes":["api:read"]}},"/bots/project-agent-provider-settings":{"post":{"operationId":"BotsController_saveProjectAgentProviderSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveProjectAgentProviderSettingsDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Save project-level hosted-agent overrides","tags":["bots"],"x-required-scopes":["api:write"]}},"/bots/project-agent-provider-settings/validate":{"post":{"operationId":"BotsController_validateProjectAgentProviderSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateProjectAgentProviderSettingsDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Validate resolved project-level hosted-agent settings","tags":["bots"],"x-required-scopes":["api:write"]}},"/workspace/manual-position-categories":{"get":{"description":"Retrieves all manual position categories for the workspace, sorted alphabetically by name.\n\n**What are Manual Position Categories?**\nManual position categories are organizational labels used to group and structure manual positions (custom invoice line items) in projects. Categories help organize manual positions for better clarity in offers and invoices, making it easier to understand different types of services or costs.\n\n**What is returned:**\n- List of all active categories with:\n  - `id`: Unique category identifier (UUID)\n  - `name`: Category name (e.g., \"Development\", \"Consulting\", \"Travel Expenses\")\n\n**How it works:**\n- Categories are workspace-wide and can be used across all projects\n- Categories are sorted alphabetically by name for consistent ordering\n- Only active (non-deleted) categories are returned\n- Categories can be assigned to manual positions when creating or updating them\n\n**Use cases:**\n- Organizing manual positions by service type (e.g., \"Development\", \"Design\", \"Consulting\")\n- Grouping costs by nature (e.g., \"Travel Expenses\", \"External Services\", \"Licenses\")\n- Improving invoice readability by categorizing line items","operationId":"ManualPositionCategoriesController_listCategories","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ManualPositionCategoryResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all manual position categories","tags":["workspace","manual-position-categories"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new manual position category or updates an existing one. The operation is determined by whether an `id` is provided in the request body.\n\n**How it works:**\n- **Create**: Omit the `id` field (or set it to `null`) to create a new category\n- **Update**: Include the `id` field with an existing category UUID to update that category\n\n**What is returned:**\n- The created or updated category with:\n  - `id`: Category identifier (UUID)\n  - `name`: Category name\n\n**Validation rules:**\n- `name`: Required, must be a non-empty string\n- `id`: Optional, must be a valid UUID if provided\n\n**Error handling:**\n- If updating with an `id` that doesn't exist, returns `404 Not Found`\n- If validation fails, returns `400 Bad Request` with detailed error messages\n\n**Note:** This endpoint requires the `manageManualPositions` permission.","operationId":"ManualPositionCategoriesController_createOrUpdateCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrUpdateManualPositionCategoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualPositionCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update manual position category","tags":["workspace","manual-position-categories"],"x-required-scopes":["api:write"]}},"/workspace/manual-position-categories/{id}":{"delete":{"description":"Soft deletes a manual position category by setting its `deletedAt` timestamp. All manual positions that were assigned to this category will have their `categoryId` automatically unset to `null`.\n\n**How it works:**\n- The category is marked as deleted (soft delete) and will no longer appear in category lists\n- All manual positions that were using this category are automatically updated to have `categoryId = null`\n- Manual positions themselves are not deleted, only their category assignment is removed\n- The category cannot be recovered after deletion\n\n**What is returned:**\n- `success`: Boolean indicating the operation completed successfully\n\n**Error handling:**\n- If the category ID doesn't exist, returns `404 Not Found`\n- The `id` parameter must be a valid UUID\n\n**Use cases:**\n- Removing obsolete categories that are no longer needed\n- Consolidating categories by removing duplicates\n- Cleaning up workspace organization\n\n**Note:** This endpoint requires the `manageManualPositions` permission.","operationId":"ManualPositionCategoriesController_deleteCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete manual position category","tags":["workspace","manual-position-categories"],"x-required-scopes":["api:write"]}},"/workspace/details":{"get":{"description":"Returns comprehensive information about the current workspace, including branding settings, features, and configuration. The workspace is the top-level organizational unit in Leadtime that contains all users, projects, tasks, and other entities. This endpoint provides essential workspace metadata such as company name, domain, enabled features, appearance settings (colors, logos), custom icons, sprint configuration, and billing status. User-specific data is excluded from this response and is available via the /users/me endpoint. The attendanceEnabled field is only true if attendance tracking is enabled for the workspace AND the current user is an Employee type. The isBillingLocked field indicates whether the workspace is locked due to past due billing status.","operationId":"WorkspaceController_details","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Workspace not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get workspace details","tags":["workspace"],"x-required-scopes":["api:read"]}},"/workspace/object-settings":{"get":{"description":"Returns all object types with nested statuses and per-type custom field definitions. Use this to build object forms and integrations without admin object-type permissions.","operationId":"WorkspaceController_objectSettings","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceObjectSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Workspace object settings (read-only)","tags":["workspace"],"x-required-scopes":["api:read"]}},"/workspace/upload":{"post":{"description":"Uploads a file to the workspace and returns file metadata including a unique file ID and public URL. This endpoint is used for uploading various file types such as logos, avatars, documents, or custom icons. The uploaded file is associated with the workspace and can be referenced in other API endpoints using the returned file ID. The file is stored securely and a public URL is generated for accessing it. Common use cases include uploading workspace logos (for light and dark themes), user avatars, or custom icon images. The optional type parameter can be used to categorize the file (e.g., \"logo\", \"avatar\", \"document\") for organizational purposes.","operationId":"WorkspaceController_uploadFile","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"File to upload"},"type":{"type":"string","description":"File type/category (e.g., \"logo\", \"avatar\", \"document\")","example":"logo"}},"required":["file"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Upload file to workspace","tags":["workspace"],"x-required-scopes":["api:write"]}},"/workspace/users":{"get":{"description":"Returns a list of all users in the workspace, including employees, organization members, and support users. This endpoint is optimized for common list data retrieval and provides essential user information for dropdowns, user pickers, and other UI components that need to display workspace users. The response includes user identification (ID, name, avatar), position/title, employee ID (if applicable), user type, login capability, and deletion status. Organization members have restricted visibility: they only see users from their own organization, not all workspace users. Use the type query parameter to filter by user type (Employee, OrganizationMember, LeadtimeSupport). By default, deleted users are included in the response; set includeDeleted=false to exclude them.","operationId":"WorkspaceController_listUsers","parameters":[{"name":"type","required":false,"in":"query","description":"Filter users by user type. Options: Employee (internal workspace employees), OrganizationMember (external organization members), LeadtimeSupport (Leadtime support staff). If not specified, all user types are returned.","schema":{"enum":["Employee","OrganizationMember","Bot","LeadtimeSupport","HelpdeskUser"],"type":"string"}},{"name":"includeDeleted","required":false,"in":"query","description":"Whether to include deleted users in the response. Default is true (deleted users are included). Set to false to exclude deleted users from the results.","schema":{"default":true,"type":"boolean"}}],"responses":{"200":{"description":"Workspace users retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkspaceUserDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all workspace users","tags":["workspace","common-lists"],"x-required-scopes":["api:read"]}},"/workspace/quick-search":{"get":{"description":"Performs a unified search across multiple workspace entity types in a single query. This endpoint searches through tasks, projects, employees, organizations, organization members, teams, contacts, and the workspace itself. The search matches the query term against entity names, titles, and other searchable fields. Results are automatically filtered based on user permissions and workspace scoping, ensuring users only see entities they have access to. Each result includes the entity type, title, metadata (including ID), color, icon, and short name (if applicable). This endpoint is commonly used for command palettes, global search functionality, and entity pickers that need to search across multiple types. The search is case-insensitive and performs partial matching on entity names and titles.","operationId":"WorkspaceController_quickSearch","parameters":[{"name":"term","required":true,"in":"query","description":"Search term to match against entity names, titles, and other searchable fields. The search is case-insensitive and performs partial matching. Can be a single word or multiple words. Examples: \"project\", \"John Smith\", \"Acme Corp\".","schema":{"example":"project","type":"string"}}],"responses":{"200":{"description":"Search results matching the query term","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/QuickSearchResultDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Quick search across workspace entities","tags":["workspace"],"x-required-scopes":["api:read"]}},"/workspace/product-list":{"get":{"description":"Returns a list of all catalog products in the workspace. Catalog products are products that are not yet assigned to any project or task. These are the base products available in the product catalog that can be added to projects or tasks later. This endpoint is optimized for common list data retrieval and provides essential product information including name, category, logo, and pricing. Products can have three types of pricing: fixed (one-time payment), subscription (recurring payment), or per-unit pricing. All three pricing fields are optional and can be null if not applicable to the product. The response is sorted alphabetically by product name. This endpoint requires the Projects.manageProducts permission to access.","operationId":"WorkspaceController_listProducts","parameters":[],"responses":{"200":{"description":"Catalog products retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProductListItemDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all catalog products","tags":["workspace","common-lists"],"x-required-scopes":["api:read"]}},"/workspace/form-templates":{"get":{"description":"Returns all form categories and form templates in the workspace. Form templates are reusable definitions that can be attached to tasks as form instances. Each template row includes metadata (name, category, tags, field count) but not individual field definitions. Use GET /workspace/form-templates/{id} to load the full field schema (types, labels, required flags, select options, settings) before attaching a template via POST /tasks/{identifier}/form-instances (body.templateId) or when building PATCH payloads: each `values` key matches the field `key` when set, otherwise the field `id` — the same strings appear as `valueKey` on GET /tasks/{identifier}/form-instances. Requires only the api:read scope (no FormTemplates.manage permission). For backward compatibility, GET /administration/form-templates-available returns the same list shape.","operationId":"WorkspaceController_listFormTemplates","parameters":[],"responses":{"200":{"description":"Form categories and templates retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceFormTemplatesResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all form templates","tags":["workspace","common-lists"],"x-required-scopes":["api:read"]}},"/workspace/form-templates/{id}":{"get":{"description":"Returns one form template including all non-deleted fields in sort order. Each field includes `type`, `label`, optional `key`, `id`, `required`, placeholder, help text, type-specific `settings` JSON, and `selectOptions` for Select/MultiSelect/Radio. When calling PATCH `/tasks/{identifier}/form-instances/{formInstanceId}`, use property names in `values` equal to `key` when present, otherwise the field `id` (GET form instances exposes this as `fields[].valueKey`). Typical flow: GET this endpoint to plan values → POST attach with this template `id` → PATCH with those keys. Requires only the api:read scope (no FormTemplates.manage permission).","operationId":"WorkspaceController_getFormTemplate","parameters":[{"name":"id","required":true,"in":"path","description":"Form template UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Form template with fields retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormTemplateDetailResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Form template not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get form template with fields","tags":["workspace","common-lists"],"x-required-scopes":["api:read"]}},"/administration/document-templates":{"get":{"description":"Returns a paginated, filterable, and sortable grid of all document templates in the workspace.\n\n**What are Document Templates?**\nDocument templates standardize recurring documents (like contracts, cover letters, requirement specs) and automatically fill them at runtime with project, client, and company data. Templates consist of static text plus variables that get replaced with actual values when generating a document.\n\n**What is returned:**\n- Template identification (ID, name, description)\n- Template metadata (language, type, creation/update dates)\n- Creator and updater information\n\n**Filtering and search:**\n- Quick search across template name and description\n- Advanced filtering by language, type, and date ranges\n- Sorting by any sortable field\n- Server-side pagination for large template lists\n\n**Note:** This endpoint requires the `DocumentTemplates.canManage` permission. Templates are used in projects to generate standardized documents with dynamic content.","operationId":"DocumentTemplatesController_listDocumentTemplates","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: name","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Template ID\n- **name** (string): Template name\n- **language** (string): Template language\n- **type** (set): Template type\n- **createdAt** (date): Creation timestamp\n- **updatedAt** (date): Last update timestamp\n- **createdBy** (string): Creator user ID\n- **updatedBy** (string): Last updater user ID\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Template ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **name** (string): Template name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **language** (string): Template language (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Template type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **updatedBy** (string): Last updater user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Template ID\n- **name**: Template name\n- **language**: Template language\n- **type**: Template type\n- **createdAt**: Creation timestamp\n- **updatedAt**: Last update timestamp\n- **createdBy**: Creator user ID\n- **updatedBy**: Last updater user ID\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, name, description, language, type, createdAt, updatedAt, createdBy, updatedBy","schema":{"example":"id,name","type":"array","items":{}}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List document templates","tags":["administration","document-templates","administration"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new document template for generating standardized documents with dynamic content.\n\n**What are Document Templates?**\nDocument templates standardize recurring documents (contracts, cover letters, requirement specs) by combining static text with variables that get automatically replaced with actual values when generating documents in projects.\n\n**Step-by-step process:**\n1. Provide template metadata (name, description, language, type)\n2. Define the template content as HTML or Markdown\n3. Optionally define custom variables for template-specific placeholders\n4. The system converts your content to internal format and stores the template\n\n**Content format:**\nYou can provide content as HTML or Markdown. The content supports:\n- Rich text formatting (headings, lists, tables, bold, italic, etc.)\n- System variables (e.g., `#project.name`, `#organization.companyName`, `#currentUser.firstName`)\n- Custom variables (defined in the `customVariables` array)\n- Conditional sections that show/hide based on variable values\n- Page breaks for document structure\n\n**Using variables in content:**\nVariables are represented as HTML spans: `<span data-type=\"variable\" data-id=\"project.name\">...</span>`\nCustom variables use the format: `<span data-type=\"variable\" data-id=\"custom.variable_name\">...</span>`\n\n**Custom variables:**\nCustom variables allow you to add template-specific fields that aren't covered by standard system variables. Each variable has a name, type (Text, Number, Date, Boolean, Select, etc.), optional default value, and can be marked as required. When generating a document, users will be prompted to fill in these values.\n\n**What is returned:**\nThe created template with content converted back to HTML format and all custom variables included.","operationId":"DocumentTemplatesController_createDocumentTemplate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDocumentTemplateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplateApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create document template","tags":["administration","document-templates","administration"],"x-required-scopes":["api:write"]}},"/administration/document-templates/{id}":{"get":{"description":"Returns the complete document template including its content and custom variables.\n\n**What is returned:**\n- Full template metadata (name, description, language, type)\n- Template content as HTML (converted from internal IDoc format)\n- All custom variables defined for this template\n- Creation and update timestamps with user information\n\n**Content format:**\nThe content is returned as HTML, which may include:\n- Static text and formatting (headings, lists, tables, etc.)\n- Variable placeholders (e.g., `<span data-type=\"variable\" data-id=\"project.name\">...</span>`)\n- Conditional sections that show/hide based on variable values\n- Page breaks for document structure\n\n**Custom variables:**\nEach template can define custom variables that extend beyond the standard system variables. These are returned with their type, default values, and whether they are required.","operationId":"DocumentTemplatesController_getDocumentTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplateApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get document template details","tags":["administration","document-templates","administration"],"x-required-scopes":["api:read"]},"put":{"description":"Updates an existing document template. All fields must be provided as this is a full replacement operation.\n\n**What gets updated:**\n- Template metadata (name, description, language, type)\n- Template content (replaced entirely with new HTML or Markdown)\n- Custom variables (replaced entirely with new array)\n\n**Content format:**\nProvide content as HTML or Markdown with support for:\n- Rich text formatting (headings, lists, tables, etc.)\n- System variables (e.g., `#project.name`, `#organization.companyName`)\n- Custom variables (defined in the `customVariables` array)\n- Conditional sections and page breaks\n\n**Using variables:**\nVariables are represented as HTML spans: `<span data-type=\"variable\" data-id=\"project.name\">...</span>`\nCustom variables use: `<span data-type=\"variable\" data-id=\"custom.variable_name\">...</span>`\n\n**Custom variables:**\nThe entire custom variables array is replaced. Each variable defines a template-specific field with name, type, optional default value, and required flag.\n\n**What is returned:**\nThe updated template with content converted to HTML format and all custom variables included.\n\n**Note:** For partial updates (only content or custom variables), use the PATCH endpoint instead.","operationId":"DocumentTemplatesController_updateDocumentTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDocumentTemplateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplateApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update document template","tags":["administration","document-templates","administration"],"x-required-scopes":["api:write"]},"patch":{"description":"Updates only the content and/or custom variables of a document template. All other fields remain unchanged.\n\n**What can be updated:**\n- Template content (optional - only if provided)\n- Custom variables (optional - only if provided)\n- Metadata fields (name, description, language, type) are NOT updated via this endpoint\n\n**Content format:**\nIf provided, content should be HTML or Markdown with support for:\n- Rich text formatting (headings, lists, tables, etc.)\n- System variables (e.g., `#project.name`, `#organization.companyName`)\n- Custom variables (defined in the `customVariables` array)\n- Conditional sections and page breaks\n\n**Using variables:**\nVariables are represented as HTML spans: `<span data-type=\"variable\" data-id=\"project.name\">...</span>`\nCustom variables use: `<span data-type=\"variable\" data-id=\"custom.variable_name\">...</span>`\n\n**Custom variables:**\nIf provided, the entire custom variables array replaces the existing one. Each variable defines a template-specific field with name, type, optional default value, and required flag.\n\n**What is returned:**\nThe updated template with content converted to HTML format and all custom variables included.\n\n**Note:** For full updates including metadata, use the PUT endpoint instead.","operationId":"DocumentTemplatesController_patchDocumentTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchDocumentTemplateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplateApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update document template","tags":["administration","document-templates","administration"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a document template from the workspace.\n\n**What happens:**\n- The template is marked as deleted (soft delete)\n- It will no longer appear in list queries\n- It cannot be used for generating new documents\n- The template data is preserved in the database\n\n**Note:** This is a soft delete operation. The template is not permanently removed and can potentially be restored if needed. This endpoint requires the `DocumentTemplates.canManage` permission.","operationId":"DocumentTemplatesController_deleteDocumentTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete document template","tags":["administration","document-templates","administration"],"x-required-scopes":["api:write"]}},"/automations":{"get":{"description":"Returns a paginated, filterable, and sortable grid of automations in the workspace. Supports filtering by scope (personal/workspace). Requires AGENT feature and either managePersonalAutomations or manageWorkspaceAutomations.","operationId":"AutomationsController_listAutomations","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: title","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Automation ID\n- **title** (string): Automation title\n- **scope** (set): Scope (personal or workspace)\n- **executionType** (set): Execution type (agent or workflow)\n- **executorUserId** (string): Executor user ID\n- **ownerUserId** (string): Owner user ID (for personal scope)\n- **isEnabled** (boolean): Whether automation is enabled\n- **createdAt** (date): Creation timestamp\n- **updatedAt** (date): Last update timestamp\n- **createdBy** (string): Creator user ID\n- **updatedBy** (string): Last updater user ID\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Automation ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **title** (string): Automation title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **scope** (set): Scope (personal or workspace) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **executionType** (set): Execution type (agent or workflow) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **executorUserId** (string): Executor user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **ownerUserId** (string): Owner user ID (for personal scope) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **isEnabled** (boolean): Whether automation is enabled (boolean filter). Available comparisons: equal, not_equal\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **updatedBy** (string): Last updater user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Automation ID\n- **title**: Automation title\n- **scope**: Scope (personal or workspace)\n- **executionType**: Execution type (agent or workflow)\n- **executorUserId**: Executor user ID\n- **ownerUserId**: Owner user ID (for personal scope)\n- **isEnabled**: Whether automation is enabled\n- **createdAt**: Creation timestamp\n- **updatedAt**: Last update timestamp\n- **createdBy**: Creator user ID\n- **updatedBy**: Last updater user ID\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, title, scope, executionType, executorUserId, ownerUserId, isEnabled, lastRunAt, lastRunStatus, createdAt, updatedAt, createdBy, updatedBy","schema":{"example":"id,title","type":"array","items":{}}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List automations","tags":["automations"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new automation. Executor must be current user or a bot. Personal scope requires managePersonalAutomations; workspace requires manageWorkspaceAutomations. userPrompt accepts HTML or Markdown (automation prompt format: basic nodes, mentions, images; no files or videos).","operationId":"AutomationsController_createAutomation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAutomationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create automation","tags":["automations"],"x-required-scopes":["api:write"]}},"/automations/{id}":{"get":{"description":"Returns the full automation including triggers. userPrompt is returned as HTML (automation prompt format: basic nodes, mentions, images; no files or videos).","operationId":"AutomationsController_getAutomation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get automation details","tags":["automations"],"x-required-scopes":["api:read"]},"put":{"description":"Updates an existing automation. Executor may remain the existing human executor. This PUT expects the full automation payload again (including title, executorUserId, and triggers). For event triggers, matching filters must be stored in triggers[].definition.filters, not executionConfig. userPrompt accepts HTML or Markdown (automation prompt format: basic nodes, mentions, images; no files or videos).","operationId":"AutomationsController_updateAutomation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAutomationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update automation","tags":["automations"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an automation.","operationId":"AutomationsController_deleteAutomation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete automation","tags":["automations"],"x-required-scopes":["api:write"]}},"/automations/{id}/test-run":{"post":{"description":"Creates and enqueues a test run for the automation. Optional testContextDoc (IDoc) is merged with the base prompt. Returns the created run id.","operationId":"AutomationsController_testRun","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestRunAutomationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRunCreatedResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create test run","tags":["automations"],"x-required-scopes":["api:write"]}},"/automations/{id}/runs":{"get":{"description":"Returns a paginated grid of runs for an automation.","operationId":"AutomationsController_listAutomationRuns","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List automation runs","tags":["automations"],"x-required-scopes":["api:read"]}},"/automations/{id}/runs/{runId}":{"get":{"description":"Returns run metadata and, when linked, agent conversation details.","operationId":"AutomationsController_getAutomationRun","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"runId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRunApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get automation run details","tags":["automations"],"x-required-scopes":["api:read"]}},"/administration/product-catalog":{"get":{"description":"**What is the Product Catalog?**\nThe product catalog in Leadtime is used for the central management of all standardizable services and products of a company. It helps you manage all your standardisable services and products in one place, structure offers, set prices, and define variations or options. The catalog is the base for sales, quotes, and billing of recurring services.\n\n**What is a Product?**\nA product is a standardized, reusable unit of service for your business. It can be a physical item, a software license, or a clearly defined service that is offered with fixed or recurring prices. Unlike projects that are tailored to customer needs, a product describes a predefined, repeatable offering that can be sold multiple times and used directly in proposals, invoices, or projects.\n\n**What data is returned:**\nThis endpoint returns a paginated, filterable, and sortable list of all catalog products in your workspace. Each product includes:\n- Basic information: name, category, logo\n- Pricing: fixed price, subscription price, per-unit price\n- Structure: number of variants and options\n- Metadata: creation date, last update\n\n**Supported operations:**\n- Filter by category, name, pricing type, or any other field\n- Sort by any column (name, price, category, etc.)\n- Search across product names\n- Paginate through large catalogs\n\nRetrieves a paginated grid of product catalog with filtering and sorting capabilities. Quick search available on: name. Filterable fields: id, name, categoryId, logoId, optionsCount, variantsCount, priceFixed, priceSubscription, pricePerUnit. Sortable fields: id, name, categoryId, optionsCount, variantsCount, priceFixed, priceSubscription, pricePerUnit.","operationId":"ProductCatalogController_listProductCatalog","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: name","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Product ID\n- **name** (string): Product name\n- **categoryId** (string): Category ID\n- **logoId** (string): Logo file ID\n- **optionsCount** (number): Number of product options\n- **variantsCount** (number): Number of product variants\n- **priceFixed** (number): Fixed price\n- **priceSubscription** (number): Subscription price\n- **pricePerUnit** (number): Price per unit\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Product ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **name** (string): Product name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **categoryId** (string): Category ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **logoId** (string): Logo file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **optionsCount** (number): Number of product options (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **variantsCount** (number): Number of product variants (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **priceFixed** (number): Fixed price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **priceSubscription** (number): Subscription price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **pricePerUnit** (number): Price per unit (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Product ID\n- **name**: Product name\n- **categoryId**: Category ID\n- **optionsCount**: Number of product options\n- **variantsCount**: Number of product variants\n- **priceFixed**: Fixed price\n- **priceSubscription**: Subscription price\n- **pricePerUnit**: Price per unit\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** name (asc)","schema":{"example":"[{\"field\":\"name\",\"direction\":\"asc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, name, categoryId, logoId, optionsCount, variantsCount, priceFixed, priceSubscription, pricePerUnit","schema":{"example":"id,name","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of product catalog items"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List product catalog","tags":["administration","products","administration"],"x-required-scopes":["api:read"]},"post":{"description":"**What this does:**\nCreates a new catalog product with variants and options. Catalog products are standardized, reusable offerings that can be added to projects, tickets, quotes, and invoices.\n\n**Product structure:**\nA product consists of:\n1. **Product details**: Name, category, logo, description, and base pricing\n2. **Variants** (optional): Different configurations or performance levels (e.g., Standard, Pro, Enterprise) with individual prices\n3. **Options** (optional): Extra services or add-ons that customers can optionally select (e.g., \"Add workshop\" or \"Enable premium support\")\n\n**Pricing:**\nYou can set one or more pricing types:\n- **Fixed price**: One-time payment (e.g., for hardware or one-time services)\n- **Subscription price**: Recurring payment (e.g., monthly SaaS subscription)\n- **Price per unit**: Flexible billing by quantity (e.g., per user, per hour)\n\n**Description format:**\n- Descriptions can be provided as HTML or Markdown\n- The system automatically converts them to internal format (IDoc) for storage\n- Supported formatting: tables, callouts, emojis, and basic text formatting\n\n**Logo upload:**\nTo add a logo to your product:\n1. First, call POST /api/public/workspace/upload to upload the image file\n2. The upload endpoint returns a file ID\n3. Use that file ID in the logoId field when creating the product\n\n**Validation:**\n- Product name is required\n- Category ID must be a valid UUID\n- At least one variant must be provided (even if just one)\n- Options array is required (can be empty)\n- If pricePerUnit is set, priceUnitName is required\n- If pricePerUnit or priceSubscription is set, priceFrequency must be 1, 3, 6, or 12 (months)\n\n**Response:**\nReturns the created product with all details, including:\n- Descriptions converted to HTML\n- Logo URL (if logo was uploaded)\n- All variants and options with their configurations","operationId":"ProductCatalogController_createProduct","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IProductApiResponse"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"name":["Name is required"],"variants[0].priceFixed":["Price must be a positive number"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create product","tags":["administration","products","administration"],"x-required-scopes":["api:write"]}},"/administration/product-catalog/{id}":{"get":{"description":"**What is returned:**\nReturns complete details for a single catalog product, including all variants, options, and pricing information. Product and variant descriptions are automatically converted from internal format (IDoc) to HTML for easy display.\n\n**Product structure:**\nA product consists of three main sections:\n1. **Product details**: Name, category, logo, description, and base pricing\n2. **Variants**: Different performance levels or configurations (e.g., Standard, Pro, Enterprise) with their own prices and descriptions\n3. **Options**: Extra services or add-ons that customers can optionally book (e.g., \"Add workshop\" or \"Enable premium support\")\n\n**Pricing models:**\n- **Fixed price**: One-time payment amount\n- **Subscription price**: Recurring amount at set intervals (monthly, quarterly, yearly)\n- **Price per unit**: Flexible billing by quantity (e.g., per user, per hour)\n\n**Response format:**\n- All descriptions are returned as HTML (converted from internal format)\n- Logo URL is included if a logo is set (points to public file endpoint)\n- Variants include their activation status\n- Options include their configuration (required, multiple selection)\n\n**Note:** This endpoint only returns catalog products (products without projectId or taskId). Products assigned to specific projects or tasks are not accessible through this endpoint.","operationId":"ProductCatalogController_getProductDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IProductApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get product details","tags":["administration","products","administration"],"x-required-scopes":["api:read"]},"put":{"description":"**What this does:**\nUpdates an existing catalog product. This is a full update - all fields must be provided, even if they are not changing. For partial updates, use the PATCH endpoint instead.\n\n**Important:**\n- This endpoint only works with catalog products (products without projectId or taskId)\n- All fields must be provided (use PATCH for partial updates)\n- Variants and options can be updated by including their IDs in the request\n- To remove variants or options, omit them from the arrays\n\n**Description format:**\n- Descriptions can be provided as HTML or Markdown\n- The system automatically converts them to internal format (IDoc) for storage\n- Supported formatting: tables, callouts, emojis, and basic text formatting\n\n**Logo upload:**\nTo change the logo:\n1. Call POST /api/public/workspace/upload to upload the new image file\n2. Use the returned file ID in the logoId field\n3. To remove the logo, set logoId to null\n\n**Variant and option updates:**\n- Include variant/option IDs to update existing ones\n- Omit IDs to create new variants/options\n- Omit variants/options entirely to remove them\n\n**Validation:**\n- Same validation rules apply as for creation\n- Product must exist and be a catalog product\n\n**Response:**\nReturns the updated product with all details, including:\n- Descriptions converted to HTML\n- Logo URL (if logo is set)\n- All variants and options with their configurations","operationId":"ProductCatalogController_updateProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IProductApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update product","tags":["administration","products","administration"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this does:**\nSoft deletes a catalog product. The product is marked as deleted but not permanently removed from the database. This allows for data recovery if needed.\n\n**Important:**\n- Only catalog products can be deleted through this endpoint (products without projectId or taskId)\n- Products that are assigned to projects or tasks cannot be deleted here\n- The product must exist in your workspace\n\n**After deletion:**\n- The product will no longer appear in the product catalog list\n- It will not be available for selection in projects, tickets, or quotes\n- Historical data (e.g., invoices that used this product) remains intact\n\n**Response:**\nReturns a success confirmation.","operationId":"ProductCatalogController_deleteProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete product","tags":["administration","products","administration"],"x-required-scopes":["api:write"]},"patch":{"description":"**What this does:**\nPartially updates a catalog product. Only the fields you provide will be updated; all other fields remain unchanged. This is useful for making small changes without resending the entire product structure.\n\n**Important:**\n- Only catalog products can be updated through this endpoint (products without projectId or taskId)\n- Omit fields you do not want to change\n- To clear optional fields (like logoId), set them to null explicitly\n\n**Description updates:**\n- If description is provided, it can be HTML or Markdown\n- The system automatically converts it to internal format (IDoc)\n- If description is omitted, the existing description remains unchanged\n\n**Variant updates:**\n- If variants array is provided, it replaces all existing variants\n- Include variant IDs to update existing variants\n- Omit IDs to create new variants\n- To keep existing variants unchanged, omit the variants field entirely\n\n**Option updates:**\n- If options array is provided, it replaces all existing options\n- Include option IDs to update existing options\n- Omit IDs to create new options\n- To keep existing options unchanged, omit the options field entirely\n\n**Logo updates:**\nTo change the logo:\n1. Call POST /api/public/workspace/upload to upload the new image\n2. Use the returned file ID in the logoId field\n3. To remove the logo, set logoId to null explicitly\n\n**Validation:**\n- Only provided fields are validated\n- If pricePerUnit is updated, priceUnitName must also be provided\n- If pricePerUnit or priceSubscription is updated, priceFrequency must be 1, 3, 6, or 12 (months)\n\n**Response:**\nReturns the updated product with all details, including:\n- Descriptions converted to HTML\n- Logo URL (if logo is set)\n- All variants and options with their configurations","operationId":"ProductCatalogController_patchProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProductDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IProductApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update product","tags":["administration","products","administration"],"x-required-scopes":["api:write"]}},"/administration/product-categories":{"get":{"description":"Retrieves all product categories configured for the workspace.\n\n**What are Product Categories?**\nProduct categories organize your product catalog into logical groups (e.g., Hardware, Software, Services, Subscriptions). They help structure products and make it easier to assign products in quotes, invoices, and projects.\n\n**Where are product categories used:**\n- Product catalog: Products are assigned to categories\n- Quotes and invoices: Categories help organize line items\n- Projects: Products added to projects retain their category\n- Tickets: Products added to tickets are organized by category\n\n**What is returned:**\n- Category ID, name, icon, and description\n- Sort order for display\n- Multilingual translations (name and description in different languages)\n- Default key (for system-defined categories)\n- Creation and deletion timestamps\n\n**Note:** This endpoint requires the manageSettings permission. Categories can be customized per workspace and support multilingual labels.","operationId":"ProductCategoriesController_getProductCategories","parameters":[],"responses":{"200":{"description":"Product categories retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProductCategoryResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List product categories","tags":["administration","product-categories"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new product category for organizing products in the catalog.\n\n**What are Product Categories?**\nProduct categories organize your product catalog into logical groups (e.g., Hardware, Software, Services, Subscriptions). They help structure products and make it easier to assign products in quotes, invoices, and projects.\n\n**How to create a product category:**\n1. Provide a name (e.g., \"Hardware\", \"Software\", \"Consulting\")\n2. Choose an icon in format :icon_name: (e.g., :computer:, :package:, :office_worker:)\n3. Optionally add a description\n4. Provide translations for multilingual support (name and description in different languages)\n\n**Icon format:**\n- Use emoji-style format: :icon_name:\n- Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:\n- Default icon is :rocket: if not specified\n\n**Multilingual support:**\n- Provide translations array with language codes (e.g., \"en\", \"de\")\n- Each translation can have a translated name and description\n- Translations appear automatically in multilingual user interfaces and documents\n\n**Note:** This endpoint requires the manageSettings permission. Once created, the category can be used when creating or editing products in the catalog.","operationId":"ProductCategoriesController_createProductCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductCategoryDto"}}}},"responses":{"201":{"description":"Product category created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create product category","tags":["administration","product-categories"],"x-required-scopes":["api:write"]}},"/administration/product-categories/{id}":{"get":{"description":"Retrieves detailed information about a specific product category by its ID.\n\n**What is returned:**\n- Category ID, name, icon, and description\n- Sort order for display\n- Multilingual translations (name and description in different languages)\n- Default key (for system-defined categories)\n- Creation and deletion timestamps\n\n**Note:** This endpoint requires the manageSettings permission. Returns 404 if the category does not exist or has been deleted.","operationId":"ProductCategoriesController_getProductCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Product category retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get product category details","tags":["administration","product-categories"],"x-required-scopes":["api:read"]},"put":{"description":"Updates all fields of an existing product category. All fields must be provided.\n\n**What can be updated:**\n- Name: Display name of the category\n- Icon: Icon in format :icon_name: (e.g., :computer:, :package:)\n- Description: Optional description of the category\n- Translations: Multilingual name and description translations\n\n**Icon format:**\n- Use emoji-style format: :icon_name:\n- Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:\n\n**Multilingual support:**\n- Provide complete translations array with language codes\n- Each translation can have a translated name and description\n- Translations appear automatically in multilingual user interfaces and documents\n\n**Note:** This endpoint requires the manageSettings permission. Use PATCH endpoint if you only want to update specific fields. All fields are required in PUT requests.","operationId":"ProductCategoriesController_updateProductCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductCategoryDto"}}}},"responses":{"200":{"description":"Product category updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update product category","tags":["administration","product-categories"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing product category. Only provided fields will be updated.\n\n**What can be updated:**\n- Name: Display name of the category (optional)\n- Icon: Icon in format :icon_name: (optional)\n- Description: Optional description of the category (optional)\n- Translations: Multilingual name and description translations (optional)\n\n**Icon format:**\n- Use emoji-style format: :icon_name:\n- Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:\n\n**Multilingual support:**\n- Provide translations array with language codes\n- Each translation can have a translated name and description\n- If translations are provided, they replace existing translations\n- Translations appear automatically in multilingual user interfaces and documents\n\n**Note:** This endpoint requires the manageSettings permission. Only fields provided in the request body will be updated. Fields not provided will remain unchanged. Returns 404 if the category does not exist.","operationId":"ProductCategoriesController_patchProductCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProductCategoryDto"}}}},"responses":{"200":{"description":"Product category updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update product category","tags":["administration","product-categories"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a product category using soft delete. The category is marked as deleted but not permanently removed.\n\n**What happens when deleting:**\n- Category is marked as deleted (soft delete)\n- Category will no longer appear in lists\n- Products assigned to this category may need to be reassigned\n- Category can potentially be restored if needed\n\n**Note:** This endpoint requires the manageSettings permission. Consider the impact on existing products before deleting a category. Returns 404 if the category does not exist.","operationId":"ProductCategoriesController_deleteProductCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Product category deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete product category","tags":["administration","product-categories"],"x-required-scopes":["api:write"]}},"/administration/product-categories/sort":{"post":{"description":"Updates the display order of product categories by providing an ordered list of category IDs.\n\n**How to reorder categories:**\n1. Get the current list of categories using GET /product-categories\n2. Create an array of category IDs in the desired display order\n3. Send the ordered array in the request body\n\n**Example:**\nIf you want categories in order: Hardware, Software, Consulting\nProvide: [\"hardware-id\", \"software-id\", \"consulting-id\"]\n\n**What happens:**\n- Categories are reordered according to the provided array\n- First ID in array becomes first category, second ID becomes second, etc.\n- Sort order is updated for all categories in the workspace\n\n**Note:** This endpoint requires the manageSettings permission. The order affects how categories appear in dropdowns and lists throughout the application.","operationId":"ProductCategoriesController_sortProductCategories","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortProductCategoriesDto"}}}},"responses":{"200":{"description":"Product categories reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder product categories","tags":["administration","product-categories"],"x-required-scopes":["api:write"]}},"/administration/product-categories/restore-defaults":{"post":{"description":"Restores the default product categories provided by Leadtime. This resets categories to the original system defaults.\n\n**What are default categories?**\nLeadtime provides pre-configured categories such as:\n- External Costs (Additional Services)\n- Software (Our Software)\n- Hardware (The Hardware products we sell)\n- Consulting (Our general consulting services)\n\n**How it works:**\n1. System default categories are restored\n2. Optionally keep custom categories you created (set keepCustom to true)\n3. If keepCustom is false, all custom categories are removed\n4. Default categories include multilingual translations (English and German)\n\n**When to use:**\n- You want to start fresh with default categories\n- You made changes and want to revert to defaults\n- You want a clean starting point for product organization\n\n**Note:** This endpoint requires the manageSettings permission. Use keepCustom: true to preserve your custom categories while restoring defaults. This action cannot be undone, so consider the impact on existing products.","operationId":"ProductCategoriesController_restoreProductCategoriesDefaults","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreProductCategoriesDto"}}}},"responses":{"200":{"description":"Product categories restored to defaults successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Restore default product categories","tags":["administration","product-categories"],"x-required-scopes":["api:write"]}},"/administration/roles":{"get":{"description":"**What are Roles?**\nRoles define what users can see and do in the workspace. Each role has a set of permissions that control access to features like creating projects, managing tasks, viewing reports, and more.\n\n**What is returned:**\nThis endpoint returns all roles available in the workspace, including:\n- **Base roles**: System-defined roles that cannot be modified (Root, CEO, Team Leader, Staff, Guest)\n- **Custom roles**: User-created roles that can be edited and deleted\n\n**Role properties:**\n- Each role includes name, description, icon, and type (normal or guest)\n- The readOnly flag indicates if a role is a base role (cannot be modified)\n- Parent role relationships show permission inheritance hierarchy\n- Roles are returned in the user's preferred language\n\n**Use cases:**\n- Display role selection dropdowns in user management interfaces\n- Show available roles when assigning permissions to users\n- Build role management interfaces that distinguish between base and custom roles","operationId":"RolesController_listRoles","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleListItem"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List roles","tags":["administration","roles"],"x-required-scopes":["api:read"]},"post":{"description":"**What this does:**\nCreates a new custom role in the workspace. Custom roles can be edited and deleted, unlike base roles which are system-defined and read-only.\n\n**Role types:**\n- **normal**: For internal team members (employees, managers, etc.)\n- **guest**: For external users like clients or partners with restricted access\n\n**Permission inheritance:**\n- Set a parent role ID to inherit permissions from that role\n- Inherited permissions can be overridden by explicitly setting them in the permissions map\n- This allows creating role variants without duplicating permission configurations\n\n**Permission validation:**\n- Permissions are automatically validated against disallowed permissions for the role type\n- Guest roles have restrictions on certain permissions (e.g., cannot manage workspace settings)\n- Invalid permissions are rejected with appropriate error messages\n\n**Required fields:**\n- name: Display name for the role\n- icon: Icon identifier (e.g., \"ri-user-star-line\" or \":person_in_tuxedo:\")\n- type: Either \"normal\" or \"guest\"\n\n**Optional fields:**\n- description: Human-readable description of the role's purpose\n- parentId: ID of parent role for permission inheritance\n- permissions: Map of permission keys to boolean values (inherited from parent if not specified)\n\n**What is returned:**\nReturns the complete role details including the generated role ID, creation timestamp, and all permissions (including inherited ones).\n\n**Use cases:**\n- Create specialized roles for specific teams or departments\n- Set up guest roles for external collaborators\n- Build role templates that can be customized per workspace","operationId":"RolesController_createRole","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoleDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create role","tags":["administration","roles"],"x-required-scopes":["api:write"]}},"/administration/permissions":{"get":{"description":"**What are Permissions?**\nPermissions are granular access controls that determine what actions users can perform. Each permission represents a specific capability like creating tasks, deleting projects, or managing employees.\n\n**What is returned:**\nThis endpoint returns all available permissions in the system, organized by functional groups:\n- **Tasks**: Create, edit, delete, move tasks, manage comments, log time\n- **Projects**: Create, edit, delete projects, manage project settings\n- **Employees**: Manage employee data, assign roles, view employee information\n- **Organizations**: View, create, edit external business partners\n- **Roles**: Create, edit, delete custom roles\n- **Company**: Manage company settings and workspace configuration\n- And many more functional areas\n\n**Permission structure:**\n- Each permission has a unique key (e.g., \"projects.create\", \"tasks.delete\")\n- Permissions are grouped by functional area for easier management\n- Permission keys use dot notation: \"category.action\"\n\n**Use cases:**\n- Build permission management interfaces that show all available permissions\n- Display permission checkboxes grouped by functional area\n- Validate permission keys before assigning them to roles\n- Generate permission documentation or help text","operationId":"RolesController_listPermissions","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PermissionItem"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List available permissions","tags":["administration","roles"],"x-required-scopes":["api:read"]}},"/administration/roles/{id}":{"get":{"description":"**What is returned:**\nReturns complete information about a specific role, including all its permissions and metadata.\n\n**Role details include:**\n- Basic information: name, description, icon, type (normal or guest)\n- Permission map: complete list of all permissions with their allowed/disallowed status\n- Inheritance: parent role ID if permissions are inherited\n- Metadata: creation date, last edit date, creator (for custom roles)\n- Read-only status: indicates if this is a base role that cannot be modified\n\n**Permission values:**\n- true: Permission is explicitly allowed\n- false: Permission is explicitly disallowed\n- undefined/missing: Permission is inherited from parent role (if parent exists)\n\n**Base roles:**\nBase roles (Root, CEO, Team Leader, Staff, Guest) are read-only and cannot be modified. Their permissions are predefined by the system.\n\n**Use cases:**\n- Display role details in role management interfaces\n- Show permission matrix for a specific role\n- Edit role permissions (for custom roles only)\n- Understand permission inheritance from parent roles","operationId":"RolesController_getRoleDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get role details","tags":["administration","roles"],"x-required-scopes":["api:read"]},"put":{"description":"**What this does:**\nCompletely replaces a custom role with new data. All fields must be provided - this is a full replacement, not a partial update.\n\n**Important restrictions:**\n- Base roles (Root, CEO, Team Leader, Staff, Guest) cannot be updated\n- Only custom roles created by users can be modified\n- Attempting to update a base role will return an error\n\n**What must be provided:**\n- name: Role display name\n- icon: Icon identifier\n- type: Role type (normal or guest)\n- description: Role description (can be empty string)\n- parentId: Parent role ID (can be undefined)\n- permissions: Complete permission map (all permissions must be specified)\n\n**Permission validation:**\n- All permissions are validated against disallowed permissions for the role type\n- Guest roles cannot have certain permissions (e.g., workspace administration)\n- Invalid permission combinations are rejected\n\n**Use cases:**\n- Completely redesign a role's permissions and properties\n- Migrate a role from one parent to another\n- Update role metadata (name, description, icon) along with permissions\n\n**Note:** For partial updates, use PATCH /roles/:id instead.","operationId":"RolesController_updateRole","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update role","tags":["administration","roles"],"x-required-scopes":["api:write"]},"patch":{"description":"**What this does:**\nUpdates only the specified fields of a custom role. Fields that are not provided remain unchanged. This is useful for making small adjustments without resending the entire role configuration.\n\n**Important restrictions:**\n- Base roles (Root, CEO, Team Leader, Staff, Guest) cannot be updated\n- Only custom roles can be modified\n- Permissions cannot be updated via this endpoint (use PUT /roles/:id/permissions or PATCH /roles/:id/permissions/:permissionKey)\n\n**What can be updated:**\n- name: Change the role display name\n- description: Update the role description\n- icon: Change the role icon\n- parentId: Change the parent role (permissions will be inherited from new parent)\n- type: Change role type between normal and guest\n\n**What happens:**\n- Only provided fields are updated\n- Unspecified fields keep their current values\n- If parentId is changed, permissions are recalculated based on the new parent\n- Role metadata (editedAt) is automatically updated\n\n**Use cases:**\n- Rename a role without changing permissions\n- Update role description or icon\n- Change role type (e.g., convert normal role to guest role)\n- Reassign parent role for permission inheritance\n\n**Note:** To update permissions, use PUT /roles/:id/permissions or PATCH /roles/:id/permissions/:permissionKey.","operationId":"RolesController_patchRole","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchRoleDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update role","tags":["administration","roles"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this does:**\nSoft deletes a custom role from the workspace. The role is marked as deleted but not permanently removed from the database.\n\n**Important restrictions:**\n- Base roles (Root, CEO, Team Leader, Staff, Guest) cannot be deleted\n- Only custom roles created by users can be deleted\n- Attempting to delete a base role will return an error\n\n**What happens to users:**\n- All users currently assigned to this role are automatically reassigned to the default Staff role\n- This ensures no users are left without a valid role assignment\n- The reassignment happens automatically and cannot be prevented\n\n**What happens to child roles:**\n- If other roles inherit from this role (have this role as parentId), they will need to be updated separately\n- Child roles are not automatically updated when parent is deleted\n- You should update child roles before deleting the parent, or reassign them to a different parent\n\n**Use cases:**\n- Remove obsolete roles that are no longer needed\n- Clean up test roles created during development\n- Consolidate roles by removing redundant ones\n- Archive roles that are temporarily not in use\n\n**Note:** This is a soft delete - the role can potentially be restored if needed (depending on your data retention policies).","operationId":"RolesController_deleteRole","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete role","tags":["administration","roles"],"x-required-scopes":["api:write"]}},"/administration/roles/{id}/permissions":{"put":{"description":"**What this does:**\nCompletely replaces the permissions map for a custom role. This is a full replacement - all permissions must be specified in the request.\n\n**Important restrictions:**\n- Base roles cannot have their permissions modified\n- Only custom roles can be updated\n- All permissions must be provided (this is a complete replacement)\n\n**Permission handling:**\n- Permissions are automatically normalized (removed if false, kept if true)\n- Permissions are validated against disallowed permissions for the role type\n- Guest roles have restrictions on certain permissions\n- Invalid permissions are rejected with error messages\n\n**What must be provided:**\n- permissions: Complete map of all permissions with boolean values\n  - true: Permission is allowed\n  - false: Permission is disallowed (will be removed from the map)\n\n**Permission inheritance:**\n- If the role has a parent, you can override inherited permissions\n- Setting a permission to false explicitly disallows it (even if parent allows it)\n- Setting a permission to true explicitly allows it (overrides parent if parent disallows)\n\n**Use cases:**\n- Bulk update all permissions for a role\n- Copy permissions from another role by providing its complete permission map\n- Reset role permissions to a known state\n- Apply permission templates to roles\n\n**Note:** For updating a single permission, use PATCH /roles/:id/permissions/:permissionKey instead.","operationId":"RolesController_replacePermissions","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplacePermissionsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Replace role permissions","tags":["administration","roles"],"x-required-scopes":["api:write"]}},"/administration/roles/{id}/permissions/{permissionKey}":{"patch":{"description":"**What this does:**\nGrants or revokes a single permission for a custom role. This is the most granular way to update role permissions - perfect for making individual permission changes.\n\n**Important restrictions:**\n- Base roles cannot have their permissions modified\n- Only custom roles can be updated\n- The permission must be valid and allowed for the role type\n- Guest roles cannot be granted certain permissions (e.g., workspace administration)\n\n**How it works:**\n- Provide the permission key in the URL path (e.g., \"projects.create\", \"tasks.delete\")\n- Set the \"allow\" field to true to grant the permission, or false to revoke it\n- The permission is validated against disallowed permissions for the role type\n- All other permissions remain unchanged\n\n**Permission inheritance:**\n- If the role has a parent that grants this permission, setting allow=true explicitly allows it\n- If the role has a parent that grants this permission, setting allow=false explicitly disallows it (overrides inheritance)\n- If the role has a parent that disallows this permission, setting allow=true explicitly allows it (overrides inheritance)\n\n**What is returned:**\nReturns the complete role details with updated permissions, showing the new state of all permissions including the one that was changed.\n\n**Use cases:**\n- Grant a specific permission to a role (e.g., allow \"projects.create\")\n- Revoke a specific permission from a role (e.g., disallow \"tasks.delete\")\n- Fine-tune role permissions one at a time\n- Build permission management UIs with individual toggles\n\n**Example:**\nTo grant \"projects.create\" permission: PATCH /roles/role-id/permissions/projects.create with {\"allow\": true}\nTo revoke \"tasks.delete\" permission: PATCH /roles/role-id/permissions/tasks.delete with {\"allow\": false}","operationId":"RolesController_togglePermission","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"permissionKey","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TogglePermissionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetailsResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Toggle single permission","tags":["administration","roles"],"x-required-scopes":["api:write"]}},"/administration/task-settings":{"get":{"description":"Retrieves the current task auto-generation settings for the workspace.\n\n**What are task auto-generation settings?**\nThese settings control whether Leadtime automatically generates task titles, summaries, and icons using AI when creating new tasks. When enabled, the system analyzes the task description and automatically creates:\n- **Title**: A concise, descriptive title based on the description\n- **Summary**: A short summary extracted from the entered text\n- **Icon**: A matching icon that visually represents the task content\n\n**What is returned:**\n- `tasksAutoGenerateTitle`: Whether AI-generated titles are enabled\n- `tasksAutoGenerateSummary`: Whether AI-generated summaries are enabled\n- `tasksAutoGenerateIcon`: Whether AI-generated icons are enabled\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. These settings affect how tasks are created throughout the workspace.","operationId":"TaskSettingsController_getTaskSettings","parameters":[],"responses":{"200":{"description":"Task auto-generation settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAutoGenerationSettingsDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get task auto-generation settings","tags":["administration","task-settings","task-settings"],"x-required-scopes":["api:read"]},"put":{"description":"Updates the task auto-generation settings for the workspace. All fields are required.\n\n**What can be updated:**\n- `tasksAutoGenerateTitle`: Enable or disable automatic title generation\n- `tasksAutoGenerateSummary`: Enable or disable automatic summary generation\n- `tasksAutoGenerateIcon`: Enable or disable automatic icon generation\n\n**How it works:**\nWhen enabled, Leadtime uses AI to analyze task descriptions and automatically generate structured content. This saves time and ensures consistent, well-structured tickets throughout the workspace.\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect how all new tasks are created in the workspace.","operationId":"TaskSettingsController_updateTaskSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAutoGenerationSettingsDto"}}}},"responses":{"200":{"description":"Task auto-generation settings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAutoGenerationSettingsDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task auto-generation settings","tags":["administration","task-settings","task-settings"],"x-required-scopes":["api:write"]}},"/administration/task-settings/custom-fields":{"get":{"description":"Retrieves all custom fields configured for tasks in the workspace.\n\n**What are custom fields?**\nCustom fields allow you to capture structured, project-specific data beyond standard task fields. They help organize information systematically and make data more consistent, reports more meaningful, and processes easier to automate.\n\n**Common use cases:**\n- Priority levels by customer group or contract type\n- Reference numbers from external systems\n- Technical parameters or version details\n- Checkboxes for internal approvals\n- Dropdowns for classifications, responsibilities, or error categories\n\n**What is returned:**\nAn array of custom field objects, each containing:\n- Field identification (ID, name, description)\n- Field type (Text, TextArea, Number, Date, Checkbox, Select, MultiSelect)\n- Sort order for display\n- Multilingual translations for field name and description\n- Select options (for Select and MultiSelect types) with their own translations\n\n**Note:** Custom fields are assigned to specific task types and appear in the task form when creating or editing tasks of those types.","operationId":"TaskSettingsController_getTaskCustomFields","parameters":[],"responses":{"200":{"description":"Task custom fields retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskCustomFieldResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get task custom fields","tags":["administration","task-settings","task-custom-fields"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new custom field for tasks. All fields are required.\n\n**Step-by-step process:**\n1. Provide a default name and description (used as fallback when no translation exists)\n2. Select the field type (Text, TextArea, Number, Date, Checkbox, Select, or MultiSelect)\n3. For Select or MultiSelect types, provide select options with default values\n4. Add multilingual translations for field name, description, and select options\n5. The field will be available to assign to task types\n\n**Field types:**\n- **Text**: Short text entries (e.g., \"Responsible reviewer\")\n- **TextArea**: Longer text entries (e.g., \"Audit result\")\n- **Number**: Numeric values\n- **Date**: Date values\n- **Checkbox**: Yes/No information\n- **Select**: Single selection from predefined options (requires selectOptions)\n- **MultiSelect**: Multiple selections from predefined options (requires selectOptions)\n\n**Multilingual support:**\nProvide translations for field name, description, and select options. Leadtime automatically displays the correct text based on the user language.\n\n**Note:** After creation, assign this custom field to specific task types to make it appear in task forms.","operationId":"TaskSettingsController_createTaskCustomField","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskCustomFieldDto"}}}},"responses":{"201":{"description":"Task custom field created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create task custom field","tags":["administration","task-settings","task-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/task-settings/custom-fields/{id}":{"put":{"description":"Updates an existing task custom field. All fields are required - provide complete field configuration.\n\n**What can be updated:**\n- Field name and description\n- Field type (note: changing type may affect existing data)\n- Select options (for Select and MultiSelect types)\n- Multilingual translations\n\n**Important:** This is a full update - all fields must be provided. Use PATCH endpoints if you need partial updates (not available for custom fields).","operationId":"TaskSettingsController_updateTaskCustomField","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskCustomFieldDto"}}}},"responses":{"200":{"description":"Task custom field updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task custom field","tags":["administration","task-settings","task-custom-fields"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a task custom field from the workspace.\n\n**Important considerations:**\n- The field will be removed from all task types where it was assigned\n- Existing task data in this field will be lost\n- This action cannot be undone\n\n**Note:** Consider removing the field from task types first if you want to preserve historical data.","operationId":"TaskSettingsController_deleteTaskCustomField","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Task custom field deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete task custom field","tags":["administration","task-settings","task-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/task-settings/custom-fields/sort":{"post":{"description":"Updates the display order of task custom fields.\n\n**How it works:**\nProvide an array of custom field IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.\n\n**Example:**\nIf you provide `[\"field-3\", \"field-1\", \"field-2\"]`, the fields will be displayed in that order in task forms.\n\n**Note:** The order affects how fields appear in task creation and editing forms.","operationId":"TaskSettingsController_sortTaskCustomFields","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortTaskCustomFieldsDto"}}}},"responses":{"200":{"description":"Task custom fields reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder task custom fields","tags":["administration","task-settings","task-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/task-settings/restore-defaults":{"post":{"description":"Restores default task types, statuses, and work activities to their original state when Leadtime was first installed.\n\n**What gets restored:**\n- Default task types (Feature, Bug, etc.) are reset to original configuration\n- Default task statuses (New, In Progress, Feedback, Done, Closed, Backlog) are reset\n- Default work activities are reset\n\n**Options:**\n- `keepCustom: false`: All custom task types, statuses, and activities are removed\n- `keepCustom: true`: Custom items are preserved, but default items are reset\n\n**Important:** This action cannot be undone. All customizations to default items will be lost.\n\n**Use case:** Use this when you want to start fresh with default configurations or when customizations have become too complex.","operationId":"TaskSettingsController_restoreDefaults","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreDefaultsDto"}}}},"responses":{"200":{"description":"Task settings restored to defaults successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Restore default task settings","tags":["administration","task-settings","task-settings"],"x-required-scopes":["api:write"]}},"/administration/task-settings/activities":{"get":{"description":"Retrieves all work activities configured for the workspace.\n\n**What are work activities?**\nWork activities categorize the type of work performed on tasks for time tracking purposes. They help track what kind of work was done (e.g., Development, Testing, Management, Design).\n\n**How they are used:**\n- When users log time on tasks, they select a work activity\n- Activities are assigned to specific task types\n- Time logs are categorized by activity for reporting and analysis\n\n**What is returned:**\nAn array of work activity objects, each containing:\n- Activity ID and name\n- Sort order for display\n- Multilingual translations for activity name\n\n**Default activities:**\nLeadtime comes with default activities like Development, Management, Design, and Testing. You can add company-specific activities to better match your workflows.","operationId":"TaskSettingsController_getWorkActivities","parameters":[],"responses":{"200":{"description":"Work activities retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkActivityResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get work activities","tags":["administration","task-settings","work-activities"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new work activity or updates an existing one. If an ID is provided, updates the existing activity; otherwise creates a new one.\n\n**Step-by-step process:**\n1. Provide a default activity name (used as fallback when no translation exists)\n2. Add multilingual translations for the activity name\n3. Optionally provide an ID to update an existing activity\n\n**Multilingual support:**\nProvide translations for activity name. Leadtime automatically displays the correct text based on the user language.\n\n**Examples of work activities:**\n- Development (for developers)\n- Management (for project leads)\n- Design (for designers)\n- Testing (for QA)\n- Review (for code reviews)\n\n**Note:** After creation, assign this activity to specific task types to make it available for time tracking.","operationId":"TaskSettingsController_saveWorkActivity","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkActivityDto"}}}},"responses":{"201":{"description":"Work activity created/updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkActivityResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update work activity","tags":["administration","task-settings","work-activities"],"x-required-scopes":["api:write"]}},"/administration/task-settings/activities/{id}":{"put":{"description":"Updates an existing work activity. All fields are required - provide complete activity configuration.\n\n**What can be updated:**\n- Activity name\n- Multilingual translations\n\n**Important:** This is a full update - all fields must be provided.","operationId":"TaskSettingsController_updateWorkActivity","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkActivityDto"}}}},"responses":{"200":{"description":"Work activity updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkActivityResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update work activity","tags":["administration","task-settings","work-activities"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a work activity from the workspace.\n\n**Important considerations:**\n- The activity will be removed from all task types where it was assigned\n- Existing time logs using this activity will retain the activity reference\n- This action cannot be undone\n\n**Note:** Consider removing the activity from task types first if you want to prevent future use.","operationId":"TaskSettingsController_deleteWorkActivity","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Work activity deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete work activity","tags":["administration","task-settings","work-activities"],"x-required-scopes":["api:write"]}},"/administration/task-settings/activities/sort":{"post":{"description":"Updates the display order of work activities.\n\n**How it works:**\nProvide an array of work activity IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.\n\n**Example:**\nIf you provide `[\"activity-2\", \"activity-1\", \"activity-3\"]`, the activities will be displayed in that order in time tracking dropdowns.\n\n**Note:** The order affects how activities appear in time entry forms and reports.","operationId":"TaskSettingsController_sortWorkActivities","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortWorkActivitiesDto"}}}},"responses":{"200":{"description":"Work activities reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder work activities","tags":["administration","task-settings","work-activities"],"x-required-scopes":["api:write"]}},"/administration/task-settings/statuses":{"get":{"description":"Retrieves all task statuses configured for the workspace.\n\n**What are task statuses?**\nTask statuses represent the progress or phase of a task in its workflow. They help track where tasks are in the process and organize work visually in Kanban boards and lists.\n\n**Default statuses:**\nLeadtime comes with standard statuses:\n- **New**: Task has been created but not started\n- **In Progress**: Work is actively being done\n- **Feedback**: Task needs input or review\n- **Resolved**: Task is completed but not yet closed\n- **Closed**: Task is finished and archived\n- **Backlog**: Task is planned for later\n\n**Custom statuses:**\nYou can create custom statuses based on these types to match your specific workflows. For example, \"Design in Progress\" (based on In Progress) or \"Awaiting Approval\" (based on Feedback).\n\n**What is returned:**\nAn array of task status objects, each containing:\n- Status ID, name, and icon\n- Status type (New, InProgress, Feedback, Resolved, Closed, Backlog)\n- Sort order for display\n- Multilingual translations for status name\n\n**Note:** Only non-deleted statuses are returned. Deleted statuses are soft-deleted and can be restored if needed.","operationId":"TaskSettingsController_getTaskStatuses","parameters":[],"responses":{"200":{"description":"Task statuses retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskStatusResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get task statuses","tags":["administration","task-settings","task-statuses"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new task status. All fields are required.\n\n**Step-by-step process:**\n1. Provide a status name (e.g., \"In Review\", \"Awaiting Approval\")\n2. Select an icon in format `:icon_name:` (e.g., `:hourglass_flowing_sand:`, `:check_mark:`)\n3. Choose the base status type (New, InProgress, Feedback, Resolved, Closed, or Backlog)\n4. Add multilingual translations for the status name\n\n**Status types:**\nEach custom status must be based on one of these types:\n- **New**: Initial state, task not started\n- **InProgress**: Active work phase\n- **Feedback**: Needs input or review\n- **Resolved**: Completed but not closed\n- **Closed**: Finished and archived\n- **Backlog**: Planned for later\n\n**Icon format:**\nIcons use emoji shortcodes in the format `:icon_name:`. Examples:\n- `:hourglass_flowing_sand:` for time-based statuses\n- `:check_mark:` for completed statuses\n- `:x:` for cancelled statuses\n- `:rocket:` for active statuses\n\n**Multilingual support:**\nProvide translations for status name. Leadtime automatically displays the correct text based on the user language.\n\n**Note:** After creation, assign this status to specific task types to make it available in their workflows.","operationId":"TaskSettingsController_saveTaskStatus","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskStatusDto"}}}},"responses":{"201":{"description":"Task status created/updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskStatusResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update task status","tags":["administration","task-settings","task-statuses"],"x-required-scopes":["api:write"]}},"/administration/task-settings/statuses/{id}":{"put":{"description":"Updates an existing task status. All fields are required - provide complete status configuration.\n\n**What can be updated:**\n- Status name\n- Icon\n- Status type\n- Multilingual translations\n\n**Important:** This is a full update - all fields must be provided. If a field is not provided, it will use the existing value. Use the PATCH endpoint for partial updates.","operationId":"TaskSettingsController_updateTaskStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskStatusDto"}}}},"responses":{"200":{"description":"Task status updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskStatusResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task status","tags":["administration","task-settings","task-statuses"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a task status from the workspace.\n\n**How soft delete works:**\n- The status is marked as deleted but not permanently removed\n- Existing tasks using this status retain the status reference\n- The status no longer appears in status lists or can be assigned to new tasks\n- The status can potentially be restored if needed\n\n**Important considerations:**\n- The status will be removed from all task types where it was assigned\n- Tasks currently using this status will keep the status but it will not be available for new assignments\n- This action should be used carefully as it affects workflows\n\n**Note:** Consider updating tasks to use a different status before deleting if you want to maintain workflow continuity.","operationId":"TaskSettingsController_deleteTaskStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Task status deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete task status","tags":["administration","task-settings","task-statuses"],"x-required-scopes":["api:write"]},"patch":{"description":"Updates specific fields of an existing task status without requiring all fields.\n\n**What can be updated:**\nAny combination of the following (all optional):\n- Status name\n- Icon\n- Status type\n- Multilingual translations\n\n**How it works:**\nOnly provide the fields you want to update. Fields not provided will remain unchanged. This is useful when you only need to update one aspect of a status (e.g., just the icon or just the name).\n\n**Example use cases:**\n- Update only the status icon\n- Add or update translations without changing other fields\n- Change status type while keeping name and icon\n\n**Note:** This endpoint merges provided fields with existing values, unlike PUT which requires all fields.","operationId":"TaskSettingsController_patchTaskStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskStatusDto"}}}},"responses":{"200":{"description":"Task status updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskStatusResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update task status","tags":["administration","task-settings","task-statuses"],"x-required-scopes":["api:write"]}},"/administration/task-settings/statuses/sort":{"post":{"description":"Updates the display order of task statuses.\n\n**How it works:**\nProvide an array of task status IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.\n\n**Example:**\nIf you provide `[\"status-new\", \"status-in-progress\", \"status-done\"]`, the statuses will be displayed in that order in Kanban boards, dropdowns, and status lists.\n\n**Note:** The order affects how statuses appear in task workflows and visual boards. Typically, you want statuses ordered to reflect the natural workflow progression.","operationId":"TaskSettingsController_sortTaskStatuses","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortTaskStatusesDto"}}}},"responses":{"200":{"description":"Task statuses reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder task statuses","tags":["administration","task-settings","task-statuses"],"x-required-scopes":["api:write"]}},"/administration/task-settings/types":{"get":{"description":"Retrieves all task types configured for the workspace.\n\n**What are task types?**\nTask types are the backbone of task organization in Leadtime. They define what kind of tickets exist in the system (e.g., Feature, Bug, Support Case) and structure how tasks are created, documented, and handled.\n\n**What a task type includes:**\n- **Icon and name**: Recognizable symbol and name (e.g., \"Server Crash\", \"Feature Request\")\n- **Base type**: Feature (value-adding work) or Bug (bug fix, typically not billable)\n- **Status options**: Which statuses can be used in this task type workflow\n- **Custom fields**: Additional structured data fields beyond standard fields\n- **Work activities**: Which time tracking activities are allowed\n- **Task template**: Pre-filled description structure for new tasks\n- **Required fields**: Fields that must be filled when creating tasks of this type\n\n**Benefits:**\n- Structure workflows with unified statuses and fields\n- Ensure complete data capture with required fields\n- Enable precise analysis and reporting\n- Customize for different departments or teams\n\n**What is returned:**\nAn array of task type objects, each containing:\n- Type identification (ID, name, icon, base type)\n- Sort order for display\n- Multilingual translations\n- Associated status IDs\n- Associated custom field IDs\n- Associated work activity IDs\n- Task template as HTML\n- Required field names\n\n**Note:** Task templates are returned as HTML, converted from the internal IDoc format.","operationId":"TaskSettingsController_getTaskTypes","parameters":[],"responses":{"200":{"description":"Task types retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskTypeResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get task types","tags":["administration","task-settings","task-types"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new task type with the specified configuration. All fields are required.\n\n**Step-by-step process:**\n1. Provide a name and icon for the task type\n2. Select the base type (Feature or Bug)\n3. Add multilingual translations\n4. Assign status IDs that can be used with this task type\n5. Assign custom field IDs that should appear for this task type\n6. Provide a task template as HTML (will be converted to internal format)\n7. Specify required field names\n\n**Base types:**\n- **Feature**: Value-adding work (typically billable)\n- **Bug**: Bug fix or maintenance (typically not billable)\n\n**Task template:**\nProvide the template as HTML. It will be automatically converted to the internal IDoc format. The template structures the description field and can include:\n- Headings and formatting\n- Tables and checklists\n- Editor placeholders (hints that disappear after saving)\n\n**Required fields:**\nSpecify which standard fields must be filled when creating tasks of this type. Common field names: \"title\", \"description\", \"assignee\", \"dueDate\".\n\n**Note:** After creation, assign this task type to projects to make it available for task creation.","operationId":"TaskSettingsController_createTaskType","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskTypeDto"}}}},"responses":{"201":{"description":"Task type created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTypeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create task type","tags":["administration","task-settings","task-types"],"x-required-scopes":["api:write"]}},"/administration/task-settings/types/{id}":{"put":{"description":"Updates an existing task type with all fields. All fields are required - provide complete task type configuration.\n\n**What can be updated:**\n- Type name and icon\n- Base type (Feature or Bug)\n- Multilingual translations\n- Associated status IDs\n- Associated custom field IDs\n- Task template (provide HTML, will be converted)\n- Required field names\n\n**Important:** This is a full update - all fields must be provided. If a field is not provided, it will use the existing value. Use the PATCH endpoint for partial updates.\n\n**Task template handling:**\nIf `templateDoc` is provided, it will replace the existing template. If not provided, the existing template is preserved.","operationId":"TaskSettingsController_updateTaskType","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskTypeDto"}}}},"responses":{"200":{"description":"Task type updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTypeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task type","tags":["administration","task-settings","task-types"],"x-required-scopes":["api:write"]},"patch":{"description":"Updates specific fields of an existing task type without requiring all fields.\n\n**What can be updated:**\nAny combination of the following (all optional):\n- Type name and icon\n- Base type (Feature or Bug)\n- Multilingual translations\n- Associated status IDs\n- Associated custom field IDs\n- Task template (provide HTML, will be converted)\n- Required field names\n\n**How it works:**\nOnly provide the fields you want to update. Fields not provided will remain unchanged. This is useful when you only need to update one aspect of a task type.\n\n**Example use cases:**\n- Update only the task template\n- Add or remove custom fields without changing other settings\n- Update translations without modifying workflow\n- Change required fields\n\n**Task template handling:**\nIf `templateDoc` is provided, it will replace the existing template. If not provided, the existing template is preserved.\n\n**Note:** This endpoint merges provided fields with existing values, unlike PUT which requires all fields.","operationId":"TaskSettingsController_patchTaskType","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTaskTypeDto"}}}},"responses":{"200":{"description":"Task type updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTypeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update task type","tags":["administration","task-settings","task-types"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a task type from the workspace.\n\n**How soft delete works:**\n- The task type is marked as deleted but not permanently removed\n- Existing tasks of this type retain the type reference\n- The task type no longer appears in type lists or can be assigned to new tasks\n- The task type can potentially be restored if needed\n\n**Important considerations:**\n- The task type will be removed from all projects where it was assigned\n- Tasks currently using this type will keep the type but it will not be available for new task creation\n- This action should be used carefully as it affects workflows\n\n**Note:** Consider updating existing tasks to use a different type before deleting if you want to maintain workflow continuity.","operationId":"TaskSettingsController_deleteTaskType","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Task type deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete task type","tags":["administration","task-settings","task-types"],"x-required-scopes":["api:write"]}},"/administration/task-settings/types/sort":{"post":{"description":"Updates the display order of task types.\n\n**How it works:**\nProvide an array of task type IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.\n\n**Example:**\nIf you provide `[\"type-feature\", \"type-bug\", \"type-support\"]`, the task types will be displayed in that order in task type dropdowns and selection lists.\n\n**Note:** The order affects how task types appear when creating new tasks or filtering by type.","operationId":"TaskSettingsController_sortTaskTypes","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortTaskTypesDto"}}}},"responses":{"200":{"description":"Task types reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder task types","tags":["administration","task-settings","task-types"],"x-required-scopes":["api:write"]}},"/administration/object-custom-fields":{"get":{"operationId":"ObjectCustomFieldsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectCustomFieldResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all object custom field definitions (admin)","tags":["administration","object-custom-fields"],"x-required-scopes":["api:read"]},"post":{"operationId":"ObjectCustomFieldsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateObjectCustomFieldDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create object custom field (admin)","tags":["administration","object-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/object-custom-fields/sort/{typeId}":{"post":{"operationId":"ObjectCustomFieldsController_saveCustomFieldSort","parameters":[{"name":"typeId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSortDTO"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder object custom fields for an object type (admin)","tags":["administration","object-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/object-custom-fields/{id}":{"put":{"operationId":"ObjectCustomFieldsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateObjectCustomFieldDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update object custom field (admin)","tags":["administration","object-custom-fields"],"x-required-scopes":["api:write"]},"delete":{"operationId":"ObjectCustomFieldsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete object custom field (admin)","tags":["administration","object-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/project-settings/categories":{"get":{"description":"Retrieves all project categories configured for the workspace.\n\n**What are Project Categories?**\nProject categories are used to sort projects by type or purpose. Examples include:\n- New client project\n- Change request\n- Service order\n\nThis categorization makes it easier to analyze project data, for example comparing revenue from existing clients versus new ones.\n\n**What is returned:**\n- Category ID, name, icon, and description\n- Sort order (for display ordering)\n- Multilingual translations (name and description in multiple languages)\n\n**Note:** All project categories support bilingual labels. The translations array contains localized names and descriptions that are automatically shown based on the user interface language.","operationId":"ProjectSettingsController_getProjectCategories","parameters":[],"responses":{"200":{"description":"Project categories retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectCategoryResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project categories","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project category for organizing projects by type or purpose.\n\n**Required fields:**\n- `name`: Display name for the category (e.g., \"Development\", \"Support\")\n- `icon`: Icon identifier in format :icon_name: (e.g., \":rocket:\", \":wrench:\")\n- `translations`: Array of translations for multilingual support\n\n**Optional fields:**\n- `description`: Additional context about when to use this category\n\n**Multilingual support:**\nProvide translations array with language codes (e.g., \"en\", \"de\") and corresponding names/descriptions. The system will automatically display the correct language based on user preferences.\n\n**Note:** Categories are workspace-specific. Once created, they appear as options when creating or editing projects.","operationId":"ProjectSettingsController_createProjectCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectCategoryDto"}}}},"responses":{"201":{"description":"Project category created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project category","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:write"]}},"/administration/project-settings/categories/{id}":{"get":{"description":"Retrieves detailed information about a specific project category by its ID.\n\n**What is returned:**\n- Category ID, name, icon, and description\n- Sort order\n- Complete translations array with all language variants\n\n**Use cases:**\n- Display category details in a form\n- Verify category exists before updating\n- Retrieve full translation set for editing","operationId":"ProjectSettingsController_getProjectCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project category retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project category details","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:read"]},"put":{"description":"Updates an existing project category. All fields must be provided (full replacement).\n\n**Required fields:**\n- `name`: Updated display name\n- `icon`: Updated icon identifier\n- `translations`: Complete translations array (all languages)\n\n**Optional fields:**\n- `description`: Updated description (can be null)\n\n**Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields. All translations must be included in the request.","operationId":"ProjectSettingsController_updateProjectCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectCategoryDto"}}}},"responses":{"200":{"description":"Project category updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project category","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing project category. Only provided fields will be updated.\n\n**Optional fields (update only what you need):**\n- `name`: Update display name\n- `icon`: Update icon identifier\n- `description`: Update or clear description\n- `translations`: Update translations (partial updates supported)\n\n**Note:** Fields not included in the request will remain unchanged. This is useful when you only need to update specific properties without fetching the full current state.","operationId":"ProjectSettingsController_patchProjectCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectCategoryDto"}}}},"responses":{"200":{"description":"Project category updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCategoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update project category","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a project category using soft delete (marked as deleted but not permanently removed).\n\n**Important considerations:**\n- The category will no longer appear in lists or be available for new projects\n- Existing projects using this category will retain their reference\n- Deleted categories can be restored using the restore defaults endpoint\n\n**Note:** This operation requires workspace settings management permission.","operationId":"ProjectSettingsController_deleteProjectCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project category deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project category","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:write"]}},"/administration/project-settings/categories/sort":{"post":{"description":"Updates the display order of project categories by providing an ordered array of category IDs.\n\n**How it works:**\n1. Provide an array of category IDs in the desired display order\n2. The first ID in the array will have sort order 0, second will have 1, etc.\n3. Categories not included in the array will maintain their current sort order\n\n**Example:**\nIf you provide [\"cat-3\", \"cat-1\", \"cat-2\"], then:\n- cat-3 will appear first\n- cat-1 will appear second\n- cat-2 will appear third\n\n**Note:** This affects the order in which categories appear in dropdowns and lists throughout the application.","operationId":"ProjectSettingsController_sortProjectCategories","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortProjectCategoriesDto"}}}},"responses":{"200":{"description":"Project categories reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder project categories","tags":["administration","project-settings","project-categories"],"x-required-scopes":["api:write"]}},"/administration/project-settings/statuses":{"get":{"description":"Retrieves all project statuses configured for the workspace.\n\n**What are Project Statuses?**\nProject statuses describe the major phases a project goes through from planning to completion. They represent the current state of work on a project.\n\n**Default status types include:**\n- Sales: Customer is interested and would like an offer\n- Requirements: Project is being specified and defined\n- Implementation: Project is being carried out technically or organizationally\n- Quality Management: Results are being checked\n- Billing: Project is being invoiced\n- Done: Job is completely finished\n\n**What is returned:**\n- Status ID, name, icon, type, and description\n- Sort order (for display ordering)\n- Multilingual translations\n\n**Note:** The status type determines billing eligibility. Only certain status types allow projects to be billed.","operationId":"ProjectSettingsController_getProjectStatuses","parameters":[],"responses":{"200":{"description":"Project statuses retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectStatusResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project statuses","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project status to represent a phase in your project workflow.\n\n**Required fields:**\n- `name`: Display name for the status (e.g., \"In Progress\", \"On Hold\")\n- `icon`: Icon identifier in format :icon_name: (e.g., \":hourglass_flowing_sand:\")\n- `type`: Status type enum value (Sales, Requirements, Implementation, QualityManagement, Billing, Done)\n- `translations`: Array of translations for multilingual support\n\n**Optional fields:**\n- `description`: Additional context about when to use this status\n\n**Status type meanings:**\n- Sales: Pre-contract phase, customer interest\n- Requirements: Specification and planning phase\n- Implementation: Active work phase\n- QualityManagement: Testing and review phase\n- Billing: Ready for invoicing\n- Done: Project completed\n\n**Note:** You can create multiple statuses with the same type to split phases into sub-phases (e.g., split Sales into Pitch, Negotiation, Contract closing).","operationId":"ProjectSettingsController_createProjectStatus","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectStatusDto"}}}},"responses":{"201":{"description":"Project status created successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project status","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:write"]}},"/administration/project-settings/statuses/{id}":{"get":{"description":"Retrieves detailed information about a specific project status by its ID.\n\n**What is returned:**\n- Status ID, name, icon, type, and description\n- Sort order\n- Complete translations array with all language variants\n\n**Use cases:**\n- Display status details in a form\n- Verify status exists before updating\n- Retrieve full translation set for editing","operationId":"ProjectSettingsController_getProjectStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectStatusResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project status details","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:read"]},"put":{"description":"Updates an existing project status. All fields must be provided (full replacement).\n\n**Required fields:**\n- `name`: Updated display name\n- `icon`: Updated icon identifier\n- `type`: Status type enum value\n- `translations`: Complete translations array (all languages)\n\n**Optional fields:**\n- `description`: Updated description (can be null)\n\n**Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields. Changing the status type may affect billing eligibility for projects using this status.","operationId":"ProjectSettingsController_updateProjectStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectStatusDto"}}}},"responses":{"200":{"description":"Project status updated successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project status","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing project status. Only provided fields will be updated.\n\n**Optional fields (update only what you need):**\n- `name`: Update display name\n- `icon`: Update icon identifier\n- `type`: Update status type (may affect billing eligibility)\n- `description`: Update or clear description\n- `translations`: Update translations (partial updates supported)\n\n**Note:** Fields not included in the request will remain unchanged. This is useful when you only need to update specific properties without fetching the full current state.","operationId":"ProjectSettingsController_patchProjectStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectStatusDto"}}}},"responses":{"200":{"description":"Project status updated successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update project status","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a project status using soft delete (marked as deleted but not permanently removed).\n\n**Important considerations:**\n- The status will no longer appear in lists or be available for new projects\n- Existing projects using this status will retain their reference\n- Deleted statuses can be restored using the restore defaults endpoint\n\n**Note:** This operation requires workspace settings management permission. Ensure you have alternative statuses available before deleting commonly used ones.","operationId":"ProjectSettingsController_deleteProjectStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project status deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project status","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:write"]}},"/administration/project-settings/statuses/sort":{"post":{"description":"Updates the display order of project statuses by providing an ordered array of status IDs.\n\n**How it works:**\n1. Provide an array of status IDs in the desired display order\n2. The first ID in the array will have sort order 0, second will have 1, etc.\n3. Statuses not included in the array will maintain their current sort order\n\n**Example:**\nIf you provide [\"status-3\", \"status-1\", \"status-2\"], then:\n- status-3 will appear first\n- status-1 will appear second\n- status-2 will appear third\n\n**Note:** This affects the order in which statuses appear in dropdowns and lists throughout the application.","operationId":"ProjectSettingsController_sortProjectStatuses","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortProjectStatusesDto"}}}},"responses":{"200":{"description":"Project statuses reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder project statuses","tags":["administration","project-settings","project-statuses"],"x-required-scopes":["api:write"]}},"/administration/project-settings/phases":{"get":{"description":"Retrieves all project phases (sales pipeline stages) configured for the workspace.\n\n**What are Project Phases?**\nProject phases map out the steps a project or lead goes through in the sales process. They are used in the integrated Sales CRM and can be customized to match your sales workflow.\n\n**Default phase types include:**\n- Not contacted: Lead has not been approached yet\n- Research: Information is being gathered about the lead\n- Contact made: Lead has been reached\n- Reminder: A follow-up is planned\n- Not reached: Could not get in touch with the lead\n- Implementation: Project is confirmed and executed\n\n**Phase type values:**\n- new: Initial/starting phase\n- inProgress: Active work phase\n- done: Completed/closed phase\n\n**What is returned:**\n- Phase ID, name, icon, type, and description\n- Sort order (for display ordering)\n- Multilingual translations\n\n**Note:** Phases affect the lanes in the Sales Kanban board. The phase type determines which section of the pipeline the opportunity appears in.","operationId":"ProjectSettingsController_getProjectPhases","parameters":[],"responses":{"200":{"description":"Project phases retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectPhaseResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project phases","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project phase (sales pipeline stage) for tracking opportunities through your sales process.\n\n**Required fields:**\n- `name`: Display name for the phase (e.g., \"Qualification\", \"Proposal Sent\")\n- `icon`: Icon identifier in format :icon_name: (e.g., \":calendar:\", \":phone:\")\n- `type`: Phase type enum value (new, inProgress, done)\n- `translations`: Array of translations for multilingual support\n\n**Optional fields:**\n- `description`: Additional context about when to use this phase\n\n**Phase type meanings:**\n- new: Initial phase for new opportunities (appears in \"New\" column of Kanban)\n- inProgress: Active phase (appears in \"In Progress\" column of Kanban)\n- done: Completed phase (appears in \"Done\" column of Kanban)\n\n**Note:** Phases are used in the Sales CRM Kanban board. Create phases that match your sales workflow to track opportunities from initial contact through to closed deals.","operationId":"ProjectSettingsController_createProjectPhase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectPhaseDto"}}}},"responses":{"201":{"description":"Project phase created successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project phase","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:write"]}},"/administration/project-settings/phases/{id}":{"get":{"description":"Retrieves detailed information about a specific project phase by its ID.\n\n**What is returned:**\n- Phase ID, name, icon, type, and description\n- Sort order\n- Complete translations array with all language variants\n\n**Use cases:**\n- Display phase details in a form\n- Verify phase exists before updating\n- Retrieve full translation set for editing","operationId":"ProjectSettingsController_getProjectPhase","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project phase retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectPhaseResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project phase details","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:read"]},"put":{"description":"Updates an existing project phase. All fields must be provided (full replacement).\n\n**Required fields:**\n- `name`: Updated display name\n- `icon`: Updated icon identifier\n- `type`: Phase type enum value (new, inProgress, done)\n- `translations`: Complete translations array (all languages)\n\n**Optional fields:**\n- `description`: Updated description (can be null)\n\n**Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields. Changing the phase type will affect which Kanban column opportunities appear in.","operationId":"ProjectSettingsController_updateProjectPhase","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectPhaseDto"}}}},"responses":{"200":{"description":"Project phase updated successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project phase","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing project phase. Only provided fields will be updated.\n\n**Optional fields (update only what you need):**\n- `name`: Update display name\n- `icon`: Update icon identifier\n- `type`: Update phase type (affects Kanban column placement)\n- `description`: Update or clear description\n- `translations`: Update translations (partial updates supported)\n\n**Note:** Fields not included in the request will remain unchanged. This is useful when you only need to update specific properties without fetching the full current state.","operationId":"ProjectSettingsController_patchProjectPhase","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectPhaseDto"}}}},"responses":{"200":{"description":"Project phase updated successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update project phase","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a project phase using soft delete (marked as deleted but not permanently removed).\n\n**Important considerations:**\n- The phase will no longer appear in lists or be available for new opportunities\n- Existing opportunities using this phase will retain their reference\n- Deleted phases can be restored using the restore defaults endpoint\n\n**Note:** This operation requires workspace settings management permission. Ensure you have alternative phases available before deleting commonly used ones.","operationId":"ProjectSettingsController_deleteProjectPhase","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project phase deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project phase","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:write"]}},"/administration/project-settings/phases/sort":{"post":{"description":"Updates the display order of project phases by providing an ordered array of phase IDs.\n\n**How it works:**\n1. Provide an array of phase IDs in the desired display order\n2. The first ID in the array will have sort order 0, second will have 1, etc.\n3. Phases not included in the array will maintain their current sort order\n\n**Example:**\nIf you provide [\"phase-3\", \"phase-1\", \"phase-2\"], then:\n- phase-3 will appear first\n- phase-1 will appear second\n- phase-2 will appear third\n\n**Note:** This affects the order in which phases appear in dropdowns and the Sales Kanban board.","operationId":"ProjectSettingsController_sortProjectPhases","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortProjectPhasesDto"}}}},"responses":{"200":{"description":"Project phases reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder project phases","tags":["administration","project-settings","project-phases"],"x-required-scopes":["api:write"]}},"/administration/project-settings/custom-fields":{"get":{"description":"Retrieves all custom fields configured for projects in the workspace.\n\n**What are Custom Fields?**\nCustom fields allow you to capture additional information about projects that matters to your company. These fields are flexible and can hold different types of data.\n\n**Supported field types:**\n- Text: Single-line text input\n- Textarea: Multi-line text input\n- Number: Numeric values\n- Date: Date selection\n- Checkbox: Boolean true/false\n- Select: Single selection from predefined options\n- MultiSelect: Multiple selections from predefined options\n\n**Common use cases:**\n- Technical information (e.g., Server name, System environment)\n- Sales-relevant data (e.g., Lead source, Probability, Region)\n- Individual extra info for special project types\n\n**What is returned:**\n- Field ID, name, description, type, and entity (always \"Project\")\n- Sort order (for display ordering)\n- Multilingual translations\n- Select options (for Select and MultiSelect types)\n\n**Note:** Custom fields are especially useful in sales CRM and add project management features with specific data points tailored to your business needs.","operationId":"ProjectSettingsController_getProjectCustomFields","parameters":[],"responses":{"200":{"description":"Project custom fields retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project custom fields","tags":["administration","project-settings","project-custom-fields"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new custom field for capturing additional project information.\n\n**Required fields:**\n- `name`: Display name for the field (e.g., \"Priority Level\", \"Region\")\n- `type`: Field type enum value (Text, Textarea, Number, Date, Checkbox, Select, MultiSelect)\n- `translations`: Array of translations for multilingual support\n\n**Optional fields:**\n- `id`: Optional ID for the field (for updating existing fields)\n- `description`: Additional context about the field\n- `selectOptions`: Required for Select and MultiSelect types - array of option objects with:\n  - `id`: Optional identifier (use value if not provided)\n  - `value`: Display value for the option\n  - `translations`: Array of translations for the option name\n- `translationsDescriptions`: Translations for the field description\n\n**Field type requirements:**\n- Select and MultiSelect types MUST include selectOptions array\n- Other types do not use selectOptions\n\n**Note:** Once created, the custom field will appear when creating or editing projects. The field can be reordered using the sort endpoint.","operationId":"ProjectSettingsController_createProjectCustomField","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectCustomFieldDto"}}}},"responses":{"201":{"description":"Project custom field created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project custom field","tags":["administration","project-settings","project-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/project-settings/custom-fields/{id}":{"put":{"description":"Updates an existing project custom field. All fields must be provided (full replacement).\n\n**Required fields:**\n- `name`: Updated display name\n- `type`: Field type enum value\n- `translations`: Complete translations array (all languages)\n\n**Optional fields:**\n- `description`: Updated description\n- `selectOptions`: Updated options array (required for Select and MultiSelect types)\n- `translationsDescriptions`: Updated description translations\n\n**Important:**\n- Changing field type may cause data loss if existing values are incompatible\n- For Select/MultiSelect types, all options must be included in the request\n- Removing options that are in use may cause validation errors\n\n**Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields.","operationId":"ProjectSettingsController_updateProjectCustomField","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectCustomFieldDto"}}}},"responses":{"200":{"description":"Project custom field updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project custom field","tags":["administration","project-settings","project-custom-fields"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a project custom field using soft delete (marked as deleted but not permanently removed).\n\n**Important considerations:**\n- The field will no longer appear in project forms\n- Existing project data stored in this field will be retained but not displayed\n- Deleted fields can be restored using the restore defaults endpoint\n\n**Note:** This operation requires workspace settings management permission. Consider exporting data before deleting fields if you need to preserve historical information.","operationId":"ProjectSettingsController_deleteProjectCustomField","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project custom field deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project custom field","tags":["administration","project-settings","project-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/project-settings/custom-fields/sort":{"post":{"description":"Updates the display order of project custom fields by providing an ordered array of field IDs.\n\n**How it works:**\n1. Provide an array of custom field IDs in the desired display order\n2. The first ID in the array will have sort order 0, second will have 1, etc.\n3. Fields not included in the array will maintain their current sort order\n\n**Example:**\nIf you provide [\"field-3\", \"field-1\", \"field-2\"], then:\n- field-3 will appear first in project forms\n- field-1 will appear second\n- field-2 will appear third\n\n**Note:** This affects the order in which custom fields appear in project creation and editing forms.","operationId":"ProjectSettingsController_sortProjectCustomFields","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortProjectCustomFieldsDto"}}}},"responses":{"200":{"description":"Project custom fields reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder project custom fields","tags":["administration","project-settings","project-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/project-settings/restore-defaults":{"post":{"description":"Restores default project categories, statuses, and phases to their original Leadtime configuration.\n\n**What gets restored:**\n- Project categories: Default categories like \"New Client Project\", \"Change Request\", \"Service Order\"\n- Project statuses: Default statuses (Sales, Requirements, Implementation, Quality Management, Billing, Done)\n- Project phases: Default sales pipeline phases (Not contacted, Contact made, Won, Closed, etc.)\n\n**Request body:**\n- `keepCustom`: Boolean flag (default: false)\n  - If false: All custom entries are deleted and defaults are restored\n  - If true: Custom entries are preserved, defaults are added alongside them\n\n**Important considerations:**\n- This operation cannot be undone\n- Existing projects will retain their current category/status/phase assignments\n- Custom fields are NOT affected by this operation\n- Deleted default entries will be restored\n\n**Use cases:**\n- Reset to clean slate after experimentation\n- Restore accidentally deleted default entries\n- Standardize workspace configuration across multiple workspaces","operationId":"ProjectSettingsController_restoreProjectSettingsDefaults","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreProjectSettingsDto"}}}},"responses":{"200":{"description":"Project settings restored to defaults successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Restore default project settings","tags":["administration","project-settings","project-settings"],"x-required-scopes":["api:write"]}},"/administration/project-settings/support-contingents/{projectId}":{"get":{"description":"Retrieves all support contingents (retainer plans) configured for a specific project.\n\n**What are Support Contingents?**\nSupport contingents are retainer plans that define allocated hours and pricing for support periods. They help manage support contracts with predefined hours, rates, and transferability of unused hours.\n\n**What is returned:**\n- All support contingents for the project, ordered by fromDate and toDate\n- Each contingent includes: title, date range, frequency, price, hours, calculated rate, and transferability\n- Date periods must not overlap within the same project\n\n**Note:** Requires WsSettings.manageSettings permission.","operationId":"ProjectSettingsController_getSupportContingents","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Support contingents retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportContingentResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List support contingents for a project","tags":["administration","project-settings","project-settings","support-contingents"],"x-required-scopes":["api:read"]}},"/administration/project-settings/support-contingents":{"post":{"description":"Creates a new support contingent (retainer plan) for a project.\n\n**Required fields:**\n- `projectId`: ID of the project\n- `title`: Name of the contingent plan\n- `fromDate`: Start date (ISO 8601 date format)\n- `toDate`: End date (ISO 8601 date format)\n- `frequency`: Frequency in months\n- `price`: Total price for the period\n- `hours`: Total hours allocated\n\n**Optional fields:**\n- `transferable`: Whether unused hours can be transferred (default: false)\n\n**Validation:**\n- Date periods must not overlap with existing contingents in the same project\n- Rate is automatically calculated as price / hours\n- All numeric values must be non-negative\n\n**Note:** Requires WsSettings.manageSettings permission.","operationId":"ProjectSettingsController_createSupportContingent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupportContingentDto"}}}},"responses":{"201":{"description":"Support contingent created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create support contingent","tags":["administration","project-settings","project-settings","support-contingents"],"x-required-scopes":["api:write"]}},"/administration/project-settings/support-contingents/{id}":{"put":{"description":"Updates an existing support contingent. All fields must be provided (full replacement).\n\n**Required fields:**\n- `title`: Updated title\n- `fromDate`: Updated start date\n- `toDate`: Updated end date\n- `frequency`: Updated frequency\n- `price`: Updated price\n- `hours`: Updated hours\n\n**Optional fields:**\n- `transferable`: Updated transferability flag\n\n**Note:** This is a full update operation. Use PATCH endpoint for partial updates.","operationId":"ProjectSettingsController_updateSupportContingent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSupportContingentDto"}}}},"responses":{"200":{"description":"Support contingent updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update support contingent","tags":["administration","project-settings","project-settings","support-contingents"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing support contingent. Only provided fields will be updated.\n\n**Optional fields (update only what you need):**\n- `title`: Update title\n- `fromDate`: Update start date\n- `toDate`: Update end date\n- `frequency`: Update frequency\n- `price`: Update price\n- `hours`: Update hours\n- `transferable`: Update transferability\n\n**Note:** Fields not included in the request will remain unchanged.","operationId":"ProjectSettingsController_patchSupportContingent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchSupportContingentDto"}}}},"responses":{"200":{"description":"Support contingent updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update support contingent","tags":["administration","project-settings","project-settings","support-contingents"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a support contingent using soft delete (marked as deleted but not permanently removed).\n\n**Important considerations:**\n- The contingent will no longer appear in lists\n- This operation cannot be undone\n\n**Note:** Requires WsSettings.manageSettings permission.","operationId":"ProjectSettingsController_deleteSupportContingent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Support contingent deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete support contingent","tags":["administration","project-settings","project-settings","support-contingents"],"x-required-scopes":["api:write"]}},"/imports-management/grid":{"get":{"description":"Retrieves a paginated, filterable, and sortable list of all CSV imports in the workspace.\n\n**What are Imports?**\nThe imports feature allows you to bulk import data from CSV files into Leadtime. You can import entities like Employees, Organizations, Projects, Tasks, and more. The system uses AI to automatically suggest field mappings, and you can preview results before executing.\n\n**What is returned:**\n- Paginated list of imports with metadata (file name, status, row/column counts, timestamps)\n- Supports server-side filtering, sorting, and pagination\n- Quick search available on file names\n\n**Import Status Values:**\n- `Uploaded`: Import created and ready for mapping/execution\n- `Processing`: Import is currently running in the background\n- `Completed`: Import finished successfully\n- `Failed`: Import encountered errors during processing\n\nRetrieves a paginated grid of imports with filtering and sorting capabilities. Quick search available on: fileName. Filterable fields: fileName, status, rowCount, columnCount, createdAt, importedAt. Sortable fields: fileName, status, rowCount, columnCount, createdAt, importedAt.","operationId":"ImportsManagementController_getImportsGrid","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: fileName","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **fileName** (string): Import file name\n- **status** (set): Import status (Uploaded, Processing, Completed, Failed)\n- **rowCount** (number): Number of rows in the imported file\n- **columnCount** (number): Number of columns in the imported file\n- **createdAt** (date): Import creation timestamp\n- **importedAt** (date): Import completion timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **fileName** (string): Import file name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **status** (set): Import status (Uploaded, Processing, Completed, Failed) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **rowCount** (number): Number of rows in the imported file (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **columnCount** (number): Number of columns in the imported file (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **createdAt** (date): Import creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **importedAt** (date): Import completion timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **fileName**: Import file name\n- **status**: Import status (Uploaded, Processing, Completed, Failed)\n- **rowCount**: Number of rows in the imported file\n- **columnCount**: Number of columns in the imported file\n- **createdAt**: Import creation timestamp\n- **importedAt**: Import completion timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** fileName, status, rowCount, columnCount, createdAt, importedAt","schema":{"example":"fileName,status","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved imports grid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportsGridResponse"}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get imports grid","tags":["imports-management"],"x-required-scopes":["api:read"]}},"/imports-management/upload":{"post":{"description":"Uploads a CSV file and automatically creates an import with AI-powered field mapping suggestions.\n\n**What this does:**\nThis is the first step in the import workflow. When you upload a CSV file, the system:\n1. Parses the CSV file and extracts column metadata (names, data types, sample values)\n2. Stores the file data and first 10 sample rows for preview\n3. Uses AI to analyze the CSV structure and suggest field mappings\n4. Generates mapping suggestions for supported entity types (Employee, Organization, Project, Task, etc.)\n5. Returns the import details with suggested mappings ready for review\n\n**Supported Entity Types:**\n- `Employee`: Import employee data (name, email, role, etc.)\n- `Organization`: Import organization/client data\n- `OrganizationMember`: Import organization membership relationships\n- `Project`: Import projects with custom fields support\n- `Task`: Import tasks with custom fields support\n- `TaskComment`: Import task comments\n\n**File Requirements:**\n- File must be CSV format (text/csv, application/csv, or text/plain MIME types)\n- First row should contain column headers\n- File will be parsed with automatic delimiter detection\n\n**AI Mapping Features:**\n- Automatically detects which columns map to which entity fields\n- Suggests value mappings for enums (e.g., \"Manager\" → \"manager\" role)\n- Handles custom fields for Projects and Tasks\n- Identifies relationships and dependencies (e.g., Organization for OrganizationMember)\n\n**Next Steps:**\nAfter upload, review the suggested mappings using GET /{importId}, then:\n1. Modify mappings if needed using PUT /{importId}/mappings\n2. Run a dry-run using POST /{importId}/dry-run to preview results\n3. Execute the import using POST /{importId}/execute to persist data\n\n**Note:** The AI mapping may not be perfect - always review and test with dry-run before executing.","operationId":"ImportsManagementController_uploadFile","parameters":[],"requestBody":{"required":true,"description":"CSV file to upload","content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"CSV file (text/csv, application/csv, or text/plain)"}}}}}},"responses":{"200":{"description":"CSV file uploaded and AI mappings generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadImportResponseDto"}}}},"400":{"description":"Invalid file type or CSV parsing error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Upload CSV file and generate AI mappings","tags":["imports-management"],"x-required-scopes":["api:write"]}},"/imports-management/{importId}":{"get":{"description":"Retrieves comprehensive information about a specific import, including all mappings, column metadata, and processing statistics.\n\n**What is returned:**\n- **Import Metadata**: File name, row/column counts, status, creation/completion timestamps, file size\n- **Current Mappings**: All field mappings in AI format, showing which CSV columns map to which entity fields\n- **Column Metadata**: Details about each CSV column (name, data type, sample values)\n- **Sample Rows**: First 10 rows of data for preview\n- **Processing Statistics**: Counts of created/updated/skipped entities (available after execution)\n\n**Use Cases:**\n- Review mappings before running a dry-run or execution\n- Check import status and progress during background processing\n- Inspect processing results after completion\n- Debug mapping issues by examining column metadata and sample data\n\n**Import Status Values:**\n- `Uploaded`: Ready for mapping configuration and execution\n- `Processing`: Currently running in the background (check `processedEntities` vs `totalToProcess` for progress)\n- `Completed`: Finished successfully (see `totalCreatedEntities`, `totalUpdatedEntities`, `totalSkippedEntities`)\n- `Failed`: Encountered errors (check error message if available)\n\n**Mappings Format:**\nMappings are returned in AI format, where each mapping specifies:\n- Entity type (e.g., \"Employee\", \"Project\")\n- Field mappings (which CSV column UUID maps to which entity field)\n- Value mappings (for enums, e.g., \"Manager\" → \"manager\")\n- Custom lookup fields (fields used to find existing records for updates)","operationId":"ImportsManagementController_getImportDetails","parameters":[{"name":"importId","required":true,"in":"path","description":"Unique identifier of the import","schema":{"example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364","type":"string"}}],"responses":{"200":{"description":"Import details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetImportDetailsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Import not found or access denied"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get import details","tags":["imports-management"],"x-required-scopes":["api:read"]}},"/imports-management/{importId}/mappings":{"put":{"description":"Replaces all field mappings for an import in a single request. This is the primary way to configure how CSV columns map to entity fields.\n\n**What this does:**\n- Deletes all existing mappings for the import\n- Creates new mappings from your request\n- Automatically adds dependency mappings (e.g., if you map OrganizationMember, it will also create Organization mapping if needed)\n- Validates that all column IDs exist in the import\n- Triggers re-validation of all mappings\n- Returns the updated mappings in AI format\n\n**AI Mapping Format:**\nEach mapping specifies an entity type and its field mappings:\n```json\n{\n  \"mappings\": [\n    {\n      \"type\": \"Employee\",\n      \"fields\": {\n        \"firstName\": { \"mapTo\": \"column-uuid-here\" },\n        \"lastName\": { \"mapTo\": \"column-uuid-here\" },\n        \"email\": { \"mapTo\": \"column-uuid-here\" },\n        \"role\": {\n          \"mapTo\": \"column-uuid-here\",\n          \"valueMappings\": { \"Manager\": \"manager\", \"Developer\": \"developer\" }\n        }\n      },\n      \"customLookupFields\": [\"email\"]  // optional: fields to use for finding existing records\n    }\n  ]\n}\n```\n\n**Field Mapping Properties:**\n- `mapTo`: **Required** - The UUID of the CSV column (get column IDs from GET /{importId})\n- `defaultValue`: Optional - Default value if CSV cell is empty\n- `valueMappings`: Optional - Map CSV values to entity values (e.g., \"Yes\" → true, \"Manager\" → \"manager\")\n\n**Custom Lookup Fields:**\nSpecify which fields should be used to find existing records for updates. If a record matches on these fields, it will be updated; otherwise, a new record will be created.\n- Can include both built-in fields (e.g., \"email\", \"firstName\") and custom fields (e.g., \"customField_abc123\")\n- If not specified, uses the entity's default `requiredToUpdate` fields\n- Example: Use \"email\" for Employees, or a custom \"externalId\" field for Projects\n\n**Important Notes:**\n- `mapTo` must be a column UUID from the import (get from GET /{importId} response), not a column name\n- All column IDs must exist in the import\n- Dependencies are automatically created (e.g., Organization for OrganizationMember, Project for Task)\n- Custom fields are referenced as `customField_{fieldId}` in field names\n- After updating, run a dry-run to validate mappings before execution","operationId":"ImportsManagementController_updateMappings","parameters":[{"name":"importId","required":true,"in":"path","description":"Unique identifier of the import","schema":{"example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateImportMappingsRequestDto"}}}},"responses":{"200":{"description":"Mappings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateImportMappingsResponseDto"}}}},"400":{"description":"Invalid mapping structure or column IDs"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Import not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update all import mappings in bulk","tags":["imports-management"],"x-required-scopes":["api:write"]}},"/imports-management/{importId}/dry-run":{"post":{"description":"Executes a dry-run to preview exactly what will happen when the import is executed, without making any changes to the database.\n\n**What this does:**\nThe dry-run performs a complete simulation of the import process:\n- Validates all field mappings and data types\n- Processes every row in the CSV file\n- Attempts to find existing records using lookup fields\n- Determines which entities will be created, updated, or skipped\n- Identifies validation errors and data quality issues\n- Returns detailed results for each entity type\n- **Does NOT persist any data** - completely safe to run multiple times\n\n**Why use dry-run?**\n- **Verify mappings**: Ensure CSV columns are correctly mapped to entity fields\n- **Check data quality**: Identify rows with missing required fields, invalid values, or format issues\n- **Estimate impact**: See how many records will be created vs updated before execution\n- **Debug issues**: Get detailed error messages explaining why certain rows will be skipped\n- **Validate lookup fields**: Confirm that custom lookup fields correctly identify existing records\n\n**What is returned:**\n- **Summary Statistics**: Total rows, counts of entities that will be created/updated/skipped, error count\n- **Detailed Results**: For each mapping type (Employee, Project, etc.), lists of:\n  - `toCreate`: New entities that will be created\n  - `toUpdate`: Existing entities that will be updated (with old and new values)\n  - `ignored`: Rows that will be skipped (with reasons)\n- **Error Details**: List of all rows with errors, including row index, data, and error message\n\n**Best Practice:**\nAlways run a dry-run after configuring mappings and before executing the import. This helps you:\n1. Catch mapping errors early\n2. Fix data quality issues in your CSV\n3. Understand the import impact\n4. Avoid accidentally creating duplicates or updating wrong records","operationId":"ImportsManagementController_dryRun","parameters":[{"name":"importId","required":true,"in":"path","description":"Unique identifier of the import","schema":{"example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364","type":"string"}}],"responses":{"200":{"description":"Dry-run completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunResponseDto"}}}},"400":{"description":"Import has no mappings or invalid state"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Import not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Preview import results without persisting data","tags":["imports-management"],"x-required-scopes":["api:write"]}},"/imports-management/{importId}/execute":{"post":{"description":"Executes the import to create and update entities in the database. This is the final step that actually persists your data.\n\n**What this does:**\n- Queues the import for background processing (avoids timeout for large files)\n- Returns immediately with queued status\n- Processes all rows asynchronously in the background\n- Creates new entities and updates existing ones based on lookup fields\n- Updates import status and progress statistics in real-time\n\n**Before Executing:**\n⚠️ **Important**: Always run a dry-run first to preview results!\n- Verify mappings are correct using GET /{importId}\n- Run POST /{importId}/dry-run to preview what will happen\n- Review dry-run results and fix any issues\n- Only execute when you're confident the results are correct\n\n**How to Track Progress:**\n1. Call this endpoint to start execution (returns immediately)\n2. Poll GET /{importId} to check status and progress\n3. Monitor these fields:\n   - `status`: Current import status\n   - `processedEntities` / `totalToProcess`: Progress percentage\n   - `totalCreatedEntities` / `totalUpdatedEntities` / `totalSkippedEntities`: Final counts\n\n**Import Status Values:**\n- `Uploaded`: Import created, ready for execution\n- `Processing`: Import is currently running (check `processedEntities` for progress)\n- `Completed`: Import finished successfully (see final statistics)\n- `Failed`: Import encountered errors (check error message if available)\n\n**Processing Performance:**\n- Processing time depends on row count and entity complexity\n- Typically 1-5 seconds per 100 rows\n- Large imports (1000+ rows) may take several minutes\n- Progress is updated in real-time, so you can monitor via GET /{importId}\n\n**What Happens During Execution:**\n- Each row is processed sequentially\n- System uses lookup fields to find existing records\n- If found: record is updated with new values\n- If not found: new record is created\n- Validation errors cause rows to be skipped\n- Dependencies are automatically created (e.g., Organization before OrganizationMember)","operationId":"ImportsManagementController_execute","parameters":[{"name":"importId","required":true,"in":"path","description":"Unique identifier of the import","schema":{"example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364","type":"string"}}],"responses":{"200":{"description":"Import queued for execution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteImportResponseDto"}}}},"400":{"description":"Import already processing or invalid state"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Import not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Execute import and persist data","tags":["imports-management"],"x-required-scopes":["api:write"]}},"/insights/project-chart":{"get":{"description":"**What are Project Insights?**\nProject Insights provides detailed analysis of working hours spent on company projects over time. This endpoint returns data that can be visualized as charts or exported to Excel, showing how time is allocated across projects, team members, task types, and individual tasks.\n\n**What data is returned:**\nThe response includes two \"sheets\" of data in JSON format (matching the Excel export structure):\n- **Summary sheet**: Wide format (pivot table style) with periods as rows and breakdown categories as columns. Perfect for quick overview and visual chart generation.\n- **Detailed sheet**: Long format (normalized/tidy data) with one row per period/breakdown combination. Ideal for further analysis, pivot tables, and BI tools.\n\n**Supported analysis options:**\n- **Project selection**: Analyze single project or compare multiple projects side-by-side\n- **Date range**: Auto-calculated from time log data if not provided, or specify custom range\n- **Model selection**: \n  - timeSeries: Shows data over time periods (trends and comparisons)\n  - total: Shows aggregated totals across entire period (distribution analysis)\n- **Period granularity**: Group data by months or weeks\n- **Breakdown options**: \n  - none: No breakdown (total hours only)\n  - byUser: Breakdown by team member\n  - byTaskType: Breakdown by task category\n  - byTask: Breakdown by individual task\n  - byProject: Breakdown by project (only for multiple projects)\n- **View style** (single project time series only):\n  - trend: Cumulative values showing progress over time (ladder-style bars)\n  - periodComparison: Period-by-period values (non-cumulative bars)\n- **Display style** (multiple projects time series only):\n  - groups: Separate lines/bars for each project (comparison view)\n  - stacks: Stacked bars showing combined effort (aggregate view)\n\n**Data structure:**\n- Headers and titles are fully localized based on user's language preference\n- All entity names (users, projects, tasks) are human-readable (never UUIDs)\n- Numeric values include proper formatting (2 decimal places)\n- Metadata includes query execution time and data processing statistics\n\n**Use cases:**\n- Generate visual charts showing project time allocation trends\n- Export to Excel for detailed analysis and reporting\n- Track team member contributions over time\n- Analyze task type distribution across projects\n- Monitor project progress against budgets and estimates","operationId":"InsightsController_getProjectChart","parameters":[{"name":"projectIds","required":true,"in":"query","description":"Array of project UUIDs to analyze. Can select single project for detailed analysis or multiple projects for comparison. At least one project is required.","schema":{"example":["7acff2df-a904-4323-adcc-c2f24a769bde"],"type":"array","items":{"type":"array"}}},{"name":"startDate","required":false,"in":"query","description":"Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the earliest time log entry in the selected projects. Used to determine the beginning of the time series or total aggregation period.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the latest time log entry in the selected projects. Must be after startDate if both are provided. Used to determine the end of the time series or total aggregation period.","schema":{"example":"2024-12-31","type":"string"}},{"name":"model","required":true,"in":"query","description":"Chart model type determining how data is aggregated and displayed. `timeSeries` shows data over time periods (trends and comparisons), while `total` shows aggregated totals across the entire period (distribution analysis). Time series is ideal for tracking progress over time, while total is better for understanding overall distribution.","schema":{"example":"timeSeries","type":"string","enum":["timeSeries","total"]}},{"name":"period","required":true,"in":"query","description":"Time period granularity for grouping data. `months` groups data by calendar month, `weeks` groups data by week. This determines the x-axis intervals in time series charts and affects how data is aggregated. Months provide broader trends, weeks provide more detailed granularity.","schema":{"example":"months","type":"string","enum":["months","weeks"]}},{"name":"breakdown","required":false,"in":"query","description":"Optional breakdown dimension for segmenting the data. `none` shows total hours without segmentation. `byUser` breaks down by team member, `byTaskType` by task category, `byTask` by individual task, and `byProject` by project (only available when multiple projects are selected). Breakdowns enable detailed analysis of how time is distributed across different dimensions.","schema":{"example":"byUser","type":"string","enum":["none","byUser","byTaskType","byTask","byProject","byValueGroups"]}},{"name":"viewStyle","required":false,"in":"query","description":"View style for single project time series charts. `trend` shows cumulative values (ladder-style bars showing progress from start to each period), while `periodComparison` shows period-by-period values (non-cumulative bars comparing each period independently). Only applicable when a single project is selected and model is timeSeries.","schema":{"example":"trend","type":"string","enum":["trend","periodComparison"]}},{"name":"displayStyle","required":false,"in":"query","description":"Display style for multiple projects time series charts. `groups` shows separate lines/bars for each project (comparison view), while `stacks` shows stacked bars combining all projects (aggregate view). Only applicable when multiple projects are selected and model is timeSeries.","schema":{"example":"groups","type":"string","enum":["groups","stacks"]}}],"responses":{"200":{"description":"Project insights data in structured JSON format with summary and detailed sheets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectChartDataStructuredResponseDto"}}}},"400":{"description":"Invalid request parameters (invalid dates, enum values, etc.)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User does not have permission to view project insights (requires Insights.viewProjectInsights)"},"404":{"description":"One or more project IDs not found or user does not have access to them"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project insights data in structured JSON format","tags":["insights"],"x-required-scopes":["api:read"]}},"/insights/staff-chart":{"get":{"description":"**What are Staff Insights?**\nStaff Insights provides detailed analysis of employee time tracking data, showing how team members allocate their working hours across projects, task types, and value categories. This helps managers understand workload distribution, identify productivity patterns, and track employee performance against goals.\n\n**What data is returned:**\nThe response includes two \"sheets\" of data in JSON format (matching the Excel export structure):\n- **Summary sheet**: \n  - For COMPARISON model: Wide format (pivot table style) with periods/staff as rows and breakdown categories as columns\n  - For TOTAL model: Categories with hours and percentage distribution\n- **Detailed sheet**: \n  - For COMPARISON model: Long format (normalized) with one row per period/staff/breakdown combination\n  - For TOTAL model: Same structure as summary (aggregated totals)\n\n**Supported analysis options:**\n- **Staff selection**: Analyze single employee or compare multiple team members\n- **Date range**: Auto-calculated from time log data if not provided, or specify custom range\n- **Model selection**: \n  - COMPARISON: Time-series analysis showing trends over time periods\n  - TOTAL: Aggregated totals across entire period (distribution analysis)\n- **Period granularity**: Required for COMPARISON model - group by months or weeks\n- **Breakdown options**: \n  - byProject: Hours allocated per project\n  - byTaskType: Hours by task category\n  - byChargeableTime: Billable vs non-billable hours with target hours comparison\n  - byValueGroups: Hours categorized by value groups (A, B, C, D)\n\n**Special features:**\n- **Chargeable time breakdown**: Includes target hours column based on employee goals and working time patterns, allowing comparison of actual vs target billable hours\n- **Goal integration**: When using chargeable time breakdown, compares actual hours against employee goals\n- **Localized output**: Headers and titles are fully localized based on user's language preference\n- **Human-readable data**: All entity names (employees, projects, task types) are human-readable (never UUIDs)\n\n**Use cases:**\n- Track individual employee productivity and time allocation\n- Compare team member workloads across projects\n- Analyze billable vs non-billable time distribution\n- Monitor employee performance against goals and targets\n- Export to Excel for detailed reporting and analysis","operationId":"InsightsController_getStaffChart","parameters":[{"name":"staffIds","required":true,"in":"query","description":"Array of staff member (employee) UUIDs to analyze. Can select single employee for detailed analysis or multiple employees for comparison. At least one employee is required. Goal data comes from employee goal settings and time tracking records.","schema":{"example":["7acff2df-a904-4323-adcc-c2f24a769bde"],"type":"array","items":{"type":"array"}}},{"name":"model","required":true,"in":"query","description":"Chart model type for staff time analysis. `COMPARISON` shows data over time periods (time-series analysis showing trends), while `TOTAL` shows aggregated totals across the entire period (distribution analysis). COMPARISON requires period granularity, TOTAL does not use periods.","schema":{"example":"comparison","type":"string","enum":["comparison","total"]}},{"name":"startDate","required":false,"in":"query","description":"Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the earliest time log entry for the selected staff members. Used to determine the beginning of the time series or total aggregation period.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the latest time log entry for the selected staff members. Must be after startDate if both are provided. Used to determine the end of the time series or total aggregation period.","schema":{"example":"2024-12-31","type":"string"}},{"name":"period","required":true,"in":"query","description":"Time period granularity for grouping staff time data. `months` groups data by calendar month, `weeks` groups data by week. Required for COMPARISON model to determine x-axis intervals in time-series charts. Not used for TOTAL model which shows aggregate distribution without time dimension.","schema":{"example":"months","type":"string","enum":["months","weeks"]}},{"name":"breakdown","required":true,"in":"query","description":"Breakdown dimension for segmenting staff time data. `byProject` breaks down hours allocated per project, `byTaskType` by task category, `byChargeableTime` by billable vs non-billable hours (includes target hours comparison), and `byValueGroups` by value categories (A, B, C, D). Breakdowns enable detailed analysis of how employee time is distributed.","schema":{"example":"byProject","type":"string","enum":["byProject","byTaskType","byChargeableTime","byValueGroups"]}}],"responses":{"200":{"description":"Staff insights data in structured JSON format with summary and detailed sheets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffChartDataStructuredResponseDto"}}}},"400":{"description":"Invalid request parameters (invalid dates, enum values, missing period for COMPARISON model, etc.)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User does not have permission to view staff insights (requires Insights.viewStaffInsights)"},"404":{"description":"One or more staff member IDs not found or user does not have access to them"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get staff insights data in structured JSON format","tags":["insights"],"x-required-scopes":["api:read"]}},"/insights/turnover-chart":{"get":{"description":"**What are Turnover Insights?**\nTurnover Insights provides detailed revenue analysis across projects, showing income from various revenue streams over time. This helps businesses understand revenue patterns, forecast future income, and analyze which projects or revenue types contribute most to the bottom line.\n\n**What data is returned:**\nThe response includes two or three \"sheets\" of data in JSON format (matching the Excel export structure):\n- **Summary sheet**: Wide format (pivot table style) with periods as rows and breakdown categories as columns. Structure varies by model and breakdown selection.\n- **Detailed sheet**: Long format (normalized) with one row per period/project/revenue type combination. Perfect for pivot tables and further analysis.\n- **Forecast sheet** (optional): Only included when includeTrend=true and forecast data exists. Contains 6-month revenue forecast based on subscription billing cycles and project deadlines.\n\n**Supported analysis options:**\n- **Project selection**: Analyze single project or compare multiple projects\n- **Date range**: Auto-calculated from invoice data if not provided, or specify custom range\n- **Model selection**: \n  - COMPARISON: Compare revenue across multiple projects side-by-side\n  - TOTAL: Aggregate revenue view showing combined totals\n- **Breakdown options** (both work with **COMPARISON** and **TOTAL**):\n  - **byProject**: In **COMPARISON**, each period shows one bar per project (project total for that month). In **TOTAL**, periods show stacked contributions by project toward the combined total.\n  - **byRevenueType**: In **COMPARISON**, each project’s bar is stacked by revenue type. In **TOTAL**, stacked layers are revenue types on the workspace aggregate.\n- **Forecast option**: includeTrend (only available for **TOTAL** model) — enables 6-month revenue forecast\n\n**Revenue types included:**\nThe analysis includes all revenue types from invoices:\n- Support (hourly billing)\n- Fixed Subscriptions (recurring fixed amounts)\n- Variable Subscriptions (recurring variable amounts)\n- Express Quotations (quick quotes)\n- Products (one-time product sales)\n- Components (product components)\n- Manual Positions (custom invoice line items)\n\n**Important validation rules:**\n- **includeTrend** is only available for **TOTAL** model (forecast uses the aggregate series)\n- At least one project must be selected\n\n**Forecast calculation:**\nWhen includeTrend=true, the forecast sheet includes:\n- 6-month forward-looking revenue projection\n- Based on active subscription billing cycles\n- Includes upcoming project deadlines and milestones\n- Helps with cash flow planning and revenue forecasting\n\n**Use cases:**\n- Track revenue trends over time\n- Compare revenue performance across projects\n- Analyze revenue distribution by type (subscriptions vs one-time)\n- Forecast future revenue based on active subscriptions\n- Export to Excel for financial reporting and analysis","operationId":"InsightsController_getTurnoverChart","parameters":[{"name":"projectIds","required":true,"in":"query","description":"Array of project UUIDs to analyze for turnover/revenue. Can select single project for detailed analysis or multiple projects for comparison. At least one project is required. Revenue data comes from invoices associated with these projects.","schema":{"example":["7acff2df-a904-4323-adcc-c2f24a769bde"],"type":"array","items":{"type":"array"}}},{"name":"startDate","required":false,"in":"query","description":"Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the earliest invoice date in the selected projects. Used to determine the beginning of the revenue analysis period.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the latest invoice date in the selected projects. Must be after startDate if both are provided. Used to determine the end of the revenue analysis period.","schema":{"example":"2024-12-31","type":"string"}},{"name":"model","required":true,"in":"query","description":"Chart model type for revenue analysis. `comparison` places projects side-by-side per period. `total` aggregates selected projects into one series per period. Use `total` with `includeTrend` for the 6-month forecast.","schema":{"example":"total","type":"string","enum":["comparison","total"]}},{"name":"breakdown","required":true,"in":"query","description":"Breakdown dimension. `byProject`: comparison mode shows each project’s total per period (grouped bars); total mode stacks project contributions on one bar per period. `byRevenueType`: comparison mode stacks revenue types within each project’s bar; total mode stacks revenue types on the aggregate.","schema":{"example":"byRevenueType","type":"string","enum":["byProject","byRevenueType"]}},{"name":"includeTrend","required":false,"in":"query","description":"Enable 6-month revenue forecast. Only available for TOTAL model. When true, includes a forecast sheet with forward-looking revenue projections based on active subscription billing cycles and upcoming project deadlines. Helps with cash flow planning and revenue forecasting.","schema":{"example":false,"type":"boolean"}}],"responses":{"200":{"description":"Turnover insights data in structured JSON format with summary, detailed, and optional forecast sheets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnoverChartDataStructuredResponseDto"}}}},"400":{"description":"Invalid request parameters (invalid dates or enum values)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User does not have permission to view turnover insights (requires Insights.viewTurnoverInsights)"},"404":{"description":"One or more project IDs not found or user does not have access to them"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get turnover insights data in structured JSON format","tags":["insights"],"x-required-scopes":["api:read"]}},"/insights/goal-chart":{"get":{"description":"**What are Goal Insights?**\nGoal Insights provides detailed analysis of employee time tracking goal progress, showing how team members perform against their attendance, booked hours, and billable hours targets. This helps managers track productivity, identify employees who need support, and ensure team members are meeting their performance goals.\n\n**What data is returned:**\nThe response includes two \"sheets\" of data in JSON format (matching the Excel export structure):\n- **Goal Progress sheet**: Employee time tracking goals with goal and actual values for:\n  - Attendance (optional, based on workspace settings)\n  - Booked hours (total time logged)\n  - Billable hours (chargeable time logged)\n- **Value Distribution sheet**: Breakdown of hours by value groups (A, B, C, D) showing how time is categorized by value\n\n**Supported display modes:**\n- **INDIVIDUAL mode**: One row per employee showing their individual goals and actuals. Perfect for tracking individual performance.\n- **TOTAL mode**: Single aggregated row with combined totals across all selected employees. Useful for team-level overview.\n\n**Time frame options:**\n- TODAY: Current day's goal progress\n- WEEK: Current week's goal progress\n- MONTH: Current month's goal progress\n- QUARTER: Current quarter's goal progress\n\n**Goal types explained:**\n- **Attendance Goal**: Target hours for employee presence (if attendance tracking is enabled)\n- **Booked Goal**: Target hours for total time logged (all work activities)\n- **Billable Goal**: Target hours for chargeable/billable work (revenue-generating activities)\n\n**Value groups:**\nHours are categorized into value groups based on project and task settings:\n- **Group A**: Highest value work (premium projects/tasks)\n- **Group B**: High value work\n- **Group C**: Standard value work\n- **Group D**: Lower value work\n\n**Special features:**\n- Attendance columns are conditionally included based on workspace settings (only shown if attendance tracking is enabled)\n- All headers and titles are fully localized based on user's language preference\n- Human-readable employee names (never UUIDs)\n- Goal vs actual comparison makes it easy to identify performance gaps\n\n**Use cases:**\n- Track individual employee goal achievement\n- Monitor team-level goal performance\n- Identify employees who need support to meet goals\n- Analyze value group distribution to optimize time allocation\n- Export to Excel for performance reviews and reporting","operationId":"InsightsController_getGoalChart","parameters":[{"name":"staffIds","required":true,"in":"query","description":"Array of staff member (employee) UUIDs to analyze. Can select single employee for detailed analysis or multiple employees for comparison. At least one employee is required. Goal data comes from employee goal settings and time tracking records.","schema":{"example":["7acff2df-a904-4323-adcc-c2f24a769bde"],"type":"array","items":{"type":"array"}}},{"name":"timeFrame","required":true,"in":"query","description":"Time period for goal analysis. `TODAY` analyzes current day's goal progress, `WEEK` analyzes current week, `MONTH` analyzes current month, and `QUARTER` analyzes current quarter. Goals are compared against actual time logged during the selected period.","schema":{"example":"month","type":"string","enum":["today","week","month","quarter"]}},{"name":"groupUsers","required":false,"in":"query","description":"Display mode for goal data. `INDIVIDUAL` shows one row per employee with their individual goals and actuals (default), perfect for tracking individual performance. `TOTAL` shows a single aggregated row with combined totals across all selected employees, useful for team-level overview.","schema":{"example":"individual","type":"string","enum":["individual","total"]}}],"responses":{"200":{"description":"Goal insights data in structured JSON format with goal progress and value distribution sheets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoalChartDataStructuredResponseDto"}}}},"400":{"description":"Invalid request parameters (invalid time frame, enum values, etc.)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User does not have permission to view goal insights (requires Insights.viewGoalInsights)"},"404":{"description":"One or more staff IDs not found or user does not have access to them"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get goal insights data in structured JSON format","tags":["insights"],"x-required-scopes":["api:read"]}},"/insights/workload-chart":{"get":{"description":"**What are Workload Insights?**\nWorkload Insights provides detailed analysis of open task volumes across projects and categories over time. Unlike Project Insights which focuses on completed work (time spent), Workload Insights examines the number of open tickets to answer forward-looking questions: \"How much work is still in the system?\" and \"Is demand for services increasing or decreasing?\"\n\n**What data is returned:**\nThe response includes two \"sheets\" of data in JSON format (matching the Excel export structure):\n- **Summary sheet**: Simple format with Period Label and Task Count columns. Quick overview of task volumes.\n- **Detailed sheet**: Long format (normalized) with one row per period/breakdown combination. Perfect for detailed analysis and pivot tables.\n\n**Supported analysis options:**\n- **Project selection**: Analyze single project or compare multiple projects\n- **Date range**: Required for OVER_TIME model, not used for TODAY model\n- **Model selection**: \n  - TODAY: Current snapshot of open tasks (similar to dashboard overview). Shows current state without time dimension.\n  - OVER_TIME: Historical trend analysis showing how workload has changed over time. Helps identify if more work is coming in than being processed.\n- **Period granularity**: Required for OVER_TIME model - group by months or weeks\n- **Breakdown options**: \n  - byProject: Task count breakdown by project\n  - byTaskType: Task count breakdown by task category\n  - byStatus: Task count breakdown by current status (New, In Progress, Feedback, etc.)\n  - byAge: Task count breakdown by age groups (how long tasks have been open)\n\n**What counts as \"open\" tasks:**\nA task is considered \"open\" when its status type is NOT Closed. This includes:\n- New tasks\n- Tasks In Progress\n- Tasks awaiting Feedback\n- Tasks in Backlog\n- Resolved tasks (not yet closed)\n\nOnly tasks with status type Closed are excluded from the analysis.\n\n**Use cases:**\n- Monitor current workload across projects\n- Track workload trends over time to identify bottlenecks\n- Analyze task distribution by type, status, or age\n- Compare workload across multiple projects\n- Identify if demand is increasing faster than capacity\n- Export to Excel for capacity planning and resource allocation\n\n**Business value:**\nWorkload Insights helps managers:\n- Understand current capacity utilization\n- Identify projects with excessive backlogs\n- Plan resource allocation based on workload trends\n- Detect early warning signs of capacity issues\n- Make data-driven decisions about hiring or project prioritization","operationId":"InsightsController_getWorkloadChart","parameters":[{"name":"projectIds","required":true,"in":"query","description":"Array of project UUIDs to analyze for turnover/revenue. Can select single project for detailed analysis or multiple projects for comparison. At least one project is required. Revenue data comes from invoices associated with these projects.","schema":{"example":["7acff2df-a904-4323-adcc-c2f24a769bde"],"type":"array","items":{"type":"array"}}},{"name":"model","required":true,"in":"query","description":"Chart model type for workload analysis. `TODAY` shows current snapshot of open tasks (similar to dashboard overview) without time dimension - perfect for understanding current capacity. `OVER_TIME` shows historical trend analysis revealing how workload has changed over time - helps identify if demand is increasing faster than capacity.","schema":{"example":"OVER_TIME","type":"string","enum":["TODAY","OVER_TIME"]}},{"name":"startDate","required":false,"in":"query","description":"Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). Required for OVER_TIME model to determine the beginning of the trend analysis. Not used for TODAY model which shows current snapshot only.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date for the analysis period in ISO 8601 format (YYYY-MM-DD). Required for OVER_TIME model to determine the end of the trend analysis. Must be after startDate if both are provided. Not used for TODAY model which shows current snapshot only.","schema":{"example":"2024-12-31","type":"string"}},{"name":"periods","required":false,"in":"query","description":"Time period granularity for grouping workload data over time. `MONTHS` groups data by calendar month, `WEEKS` groups data by week. Required for OVER_TIME model to determine x-axis intervals. Not used for TODAY model. Months provide broader trends, weeks provide more detailed granularity.","schema":{"example":"MONTHS","type":"string","enum":["MONTHS","WEEKS"]}},{"name":"breakdownBy","required":false,"in":"query","description":"Optional breakdown dimension for segmenting workload data. `PROJECT` breaks down by project, `TASK_TYPE` by task category, `STATUS` by current task status (New, In Progress, etc.), and `AGE` by how long tasks have been open (age groups). Breakdowns help identify bottlenecks and distribution patterns.","schema":{"example":"TASK_TYPE","type":"string","enum":["PROJECT","TASK_TYPE","STATUS","AGE"]}}],"responses":{"200":{"description":"Workload insights data in structured JSON format with summary and detailed sheets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkloadChartDataStructuredResponseDto"}}}},"400":{"description":"Invalid request parameters (invalid dates, enum values, missing required fields for OVER_TIME model, etc.)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User does not have permission to view workload insights (requires Insights.viewWorkloadInsights)"},"404":{"description":"One or more project IDs not found or user does not have access to them"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get workload insights data in structured JSON format","tags":["insights"],"x-required-scopes":["api:read"]}},"/administration/email-accounts":{"get":{"description":"Retrieves all email accounts configured for the workspace. Email accounts enable sending and receiving emails through the platform, including integration with helpdesk functionality.\n\n**Account Types:**\n- **IMAP**: Traditional email accounts with IMAP/SMTP configuration. Can be fully managed via API (create, update, delete).\n- **Google**: Gmail/Google Workspace accounts via OAuth2. Can only be viewed and deleted via API for security reasons.\n- **Office365**: Microsoft 365 accounts via OAuth2. Can only be viewed and deleted via API for security reasons.\n\n**Response Details:**\n- Returns all accounts with their current status (active, token_expired, smtp_error, imap_error, disconnected)\n- Sensitive fields (passwords, tokens) are excluded from responses\n- Each account includes capabilities indicating what operations are allowed via API\n- Supports optional filtering by account type using the `type` query parameter","operationId":"EmailAccountsController_listEmailAccounts","parameters":[{"name":"type","required":false,"in":"query","description":"Optional filter to return only accounts of a specific type. Omit this parameter to retrieve all account types.","schema":{"enum":["imap","google","office365"],"type":"string"}}],"responses":{"200":{"description":"Email accounts retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EmailAccountResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List email accounts","tags":["administration","email-accounts"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new IMAP/SMTP email account for sending and receiving emails.\n\n**Important Restrictions:**\n- Only IMAP/SMTP accounts can be created via API\n- OAuth accounts (Google, Office365) must be configured through the web application for security reasons\n\n**Required Configuration:**\n- Email address (must be unique within the workspace)\n- IMAP server settings (host, port, username, password) for receiving emails\n- SMTP server settings (host, port, username, password) for sending emails\n- Optional display name for the account\n\n**Validation:**\n- Email address must be valid and unique within the workspace\n- Ports must be between 1-65535\n- All server configuration fields are required\n\n**Response:**\n- Returns the created account with all details (excluding passwords)\n- Account status will be set to \"active\" upon successful creation","operationId":"EmailAccountsController_createEmailAccount","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmailAccountDto"}}}},"responses":{"201":{"description":"Email account created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountResponseDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create IMAP email account","tags":["administration","email-accounts"],"x-required-scopes":["api:write"]}},"/administration/email-accounts/{id}":{"get":{"description":"Retrieves detailed information about a specific email account by ID.\n\n**Response Includes:**\n- Account identification (ID, email, display name)\n- Account type (IMAP, Google, Office365)\n- Current status and last error message (if any)\n- Timestamps (created, last updated)\n- Capabilities indicating what operations are allowed via API\n\n**Security:**\n- Sensitive fields (passwords, OAuth tokens) are excluded from responses\n- For IMAP accounts, configuration details are visible but passwords are masked\n- For OAuth accounts, only basic information is returned","operationId":"EmailAccountsController_getEmailAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Email account retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get email account details","tags":["administration","email-accounts"],"x-required-scopes":["api:read"]},"put":{"description":"Fully updates an existing IMAP email account. This is a full replacement operation - all fields must be provided.\n\n**Important Restrictions:**\n- Only IMAP/SMTP accounts can be updated via API\n- Attempting to update OAuth accounts (Google, Office365) will result in a 403 Forbidden error\n- OAuth accounts must be updated through the web application for security reasons\n\n**Update Process:**\n- All fields must be provided (this replaces the entire account configuration)\n- Email address can be changed (must remain unique within workspace)\n- Server settings (IMAP/SMTP) can be updated\n- Passwords can be updated by providing new values\n\n**Validation:**\n- Same validation rules apply as for account creation\n- Email must be unique if changed\n- All required fields must be present\n\n**Response:**\n- Returns the updated account with all details (excluding passwords)\n- Account status may change based on new configuration","operationId":"EmailAccountsController_updateEmailAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailAccountDto"}}}},"responses":{"200":{"description":"Email account updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountResponseDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"OAuth accounts cannot be updated via API"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update IMAP email account","tags":["administration","email-accounts"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing IMAP email account. Only the fields you provide will be updated; all other fields remain unchanged.\n\n**Important Restrictions:**\n- Only IMAP/SMTP accounts can be updated via API\n- Attempting to update OAuth accounts (Google, Office365) will result in a 403 Forbidden error\n- OAuth accounts must be updated through the web application for security reasons\n\n**Update Process:**\n- Provide only the fields you want to update\n- Fields not provided will retain their current values\n- You can update individual settings (e.g., just the SMTP password) without changing other fields\n- Email address can be changed (must remain unique within workspace)\n\n**Common Use Cases:**\n- Update password after expiration\n- Change server hostname or port\n- Update display name\n- Modify individual IMAP or SMTP settings\n\n**Validation:**\n- Only provided fields are validated\n- Email must be unique if changed\n- Port values must be between 1-65535 if provided\n\n**Response:**\n- Returns the updated account with all details (excluding passwords)","operationId":"EmailAccountsController_patchEmailAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchEmailAccountDto"}}}},"responses":{"200":{"description":"Email account updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountResponseDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"OAuth accounts cannot be updated via API"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update IMAP email account","tags":["administration","email-accounts"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an email account from the workspace. The account is marked as deleted but not permanently removed from the database.\n\n**Supported Account Types:**\n- Works for all account types (IMAP, Google, Office365)\n- Unlike create/update operations, deletion is allowed for OAuth accounts via API\n\n**Deletion Process:**\n- Account is marked with a deletion timestamp\n- Account will no longer appear in listing endpoints\n- Associated data may be retained for audit purposes\n- Account cannot be used for sending/receiving emails after deletion\n\n**Response:**\n- Returns a success confirmation\n- Returns 404 if account does not exist","operationId":"EmailAccountsController_deleteEmailAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Email account deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete email account","tags":["administration","email-accounts"],"x-required-scopes":["api:write"]}},"/administration/email-accounts/{id}/test-sending":{"post":{"description":"Sends a test email to verify that the account's SMTP configuration is working correctly.\n\n**Purpose:**\n- Validates SMTP server connection and authentication\n- Tests email delivery functionality\n- Helps troubleshoot email sending issues\n\n**Process:**\n- Connects to the account's SMTP server using configured credentials\n- Sends a test email to the specified recipient address\n- Returns success or failure with error details\n\n**Supported Account Types:**\n- Works best with IMAP accounts (full SMTP configuration)\n- OAuth accounts (Google, Office365) may have limitations depending on token status\n\n**Request:**\n- Provide the email address where the test email should be sent\n\n**Response:**\n- Success: Confirms email was sent successfully\n- Failure: Returns error message describing what went wrong (connection issues, authentication failures, etc.)","operationId":"EmailAccountsController_testEmailSending","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEmailSendingDto"}}}},"responses":{"200":{"description":"Test email sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEmailSendingResponseDto"}}}},"400":{"description":"Failed to send test email"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Test email sending","tags":["administration","email-accounts"],"x-required-scopes":["api:write"]}},"/administration/email-accounts/{id}/test-receiving":{"post":{"description":"Tests the account's IMAP configuration by connecting to the email server and retrieving the latest email from the inbox.\n\n**Purpose:**\n- Validates IMAP server connection and authentication\n- Tests email retrieval functionality\n- Helps troubleshoot email receiving issues\n- Verifies that the account can access the mailbox\n\n**Process:**\n- Connects to the account's IMAP server using configured credentials\n- Retrieves the most recent email from the inbox\n- Returns email metadata (message ID, subject, sender, date)\n- Returns success or failure with error details\n\n**Supported Account Types:**\n- Works best with IMAP accounts (full IMAP configuration)\n- OAuth accounts (Google, Office365) may have limitations depending on token status\n\n**Response:**\n- Success: Returns details of the latest email (message ID, subject, sender, date)\n- Failure: Returns error message describing what went wrong (connection issues, authentication failures, no emails found, etc.)","operationId":"EmailAccountsController_testEmailReceiving","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully connected and retrieved email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEmailReceivingResponseDto"}}}},"400":{"description":"Failed to connect or retrieve email"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Test email receiving","tags":["administration","email-accounts"],"x-required-scopes":["api:write"]}},"/administration/vat-rates":{"get":{"description":"Retrieves all VAT (Value Added Tax) rates configured for the workspace.\n\n**What are VAT rates?**\nVAT rates determine the tax percentage applied to invoices and quotes. Since tax rates can change by law, Leadtime allows you to configure multiple VAT periods with different effective dates. The system automatically uses the correct rate based on the date when creating invoices and quotes.\n\n**What is returned:**\n- All VAT rates for the workspace, sorted by effective date (dateFrom)\n- Each rate includes: ID, VAT percentage (0-100), effective start date, and whether it is the default rate\n\n**How VAT rates work:**\n- When creating an invoice or quote, the system finds the VAT rate with the most recent dateFrom that is less than or equal to the current date\n- Documents that have already been issued keep their original tax rate\n- You can plan future VAT changes by creating rates with future effective dates\n\n**Note:** This endpoint requires the WsSettings.manageSettings permission.","operationId":"VatRatesController_listVatRates","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VatRateResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all VAT rates","tags":["administration","vat-rates"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new VAT rate for the workspace. This allows you to configure tax rates that will be automatically applied to invoices and quotes based on their creation date.\n\n**Use cases:**\n- Set up a new VAT rate that becomes effective on a specific date\n- Plan future VAT changes in advance (e.g., when tax laws change)\n- Configure historical VAT rates for accurate invoicing\n\n**How to create a VAT rate:**\n1. Provide the VAT percentage (0-100, e.g., 21 for 21%)\n2. Set the effective date (dateFrom) when this rate should start applying\n3. Optionally mark it as the default rate\n\n**Validation rules:**\n- VAT percentage must be between 0 and 100\n- dateFrom must be a valid date\n- The system validates that dateFrom does not overlap with existing VAT rates\n- If validation fails, you will receive an error message explaining the issue\n\n**Important notes:**\n- Once the effective date arrives, the new rate automatically applies to all invoices and quotes created from that point forward\n- Documents already issued keep their original tax rate\n- The system finds the correct VAT rate by selecting the one with the most recent dateFrom that is less than or equal to the invoice/quote creation date\n\n**Note:** This endpoint requires the WsSettings.manageSettings permission.","operationId":"VatRatesController_createVatRate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVatRateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create VAT rate","tags":["administration","vat-rates"],"x-required-scopes":["api:write"]}},"/administration/vat-rates/{id}":{"put":{"description":"Updates an existing VAT rate with a new percentage and/or effective date.\n\n**What can be updated:**\n- VAT percentage (0-100)\n- Effective date (dateFrom)\n\n**Validation rules:**\n- VAT percentage must be between 0 and 100\n- dateFrom must be a valid date\n- The system validates that the updated dateFrom does not overlap with other VAT rates\n- If the VAT rate does not exist, a NotFoundException is returned\n- If validation fails, you will receive an error message explaining the issue\n\n**Important notes:**\n- Changes to the effective date affect which invoices/quotes use this rate\n- Documents already issued keep their original tax rate\n- The system automatically recalculates which rate applies based on the updated dateFrom\n\n**Note:** This endpoint requires the WsSettings.manageSettings permission.","operationId":"VatRatesController_updateVatRate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVatRateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update VAT rate","tags":["administration","vat-rates"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a VAT rate from the workspace.\n\n**Restrictions:**\n- Default VAT rates cannot be deleted (isDefault: true)\n- Only non-default rates can be removed\n\n**What happens after deletion:**\n- The VAT rate is permanently removed from the workspace\n- Documents that were already issued with this rate are not affected\n- The system will use the next available VAT rate based on dateFrom when creating new invoices/quotes\n\n**Use cases:**\n- Remove a planned VAT rate that is no longer needed\n- Clean up incorrect or duplicate VAT rate entries\n\n**Note:** This endpoint requires the WsSettings.manageSettings permission. If you attempt to delete a default rate, the operation will be silently ignored.","operationId":"VatRatesController_deleteVatRate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete VAT rate","tags":["administration","vat-rates"],"x-required-scopes":["api:write"]}},"/help-center/search":{"get":{"description":"Searches the Leadtime Gleap help center for matching articles.\n\n**Use cases:**\n- Build in-product help search\n- Suggest relevant documentation from API clients\n- Resolve article identifiers before fetching the full article body\n\n**What is returned:**\n- Gleap article ID\n- Localized title\n- Short description\n- Help center collection ID\n- Human-friendly document number\n- Search relevance score\n\nUse `GET /help-center/article` with the returned `articleId` and `helpcenterCollection` values to fetch the full article content.","operationId":"HelpCenterController_searchHelpCenter","parameters":[{"name":"searchTerm","required":true,"in":"query","description":"Search term used to find relevant help center articles.","schema":{"example":"task types","type":"string"}}],"responses":{"200":{"description":"Matching help center articles","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpCenterSearchResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"502":{"description":"Gleap help center request failed upstream"},"503":{"description":"Gleap help center integration is not configured on this server"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Search help center articles","tags":["help-center","common-lists"],"x-required-scopes":["api:read"]}},"/help-center/article":{"get":{"description":"Fetches the plain-text content of a Gleap help center article.\n\n**How to use this endpoint:**\n1. Call `GET /help-center/search` to find a matching article\n2. Pass the returned `articleId` and `helpcenterCollection` to this endpoint\n3. Optionally set `language` to `de` or `en`\n\n**What is returned:**\n- Localized title and description\n- Plain-text article content\n- Draft flag\n- Tags\n- Last update timestamp","operationId":"HelpCenterController_getHelpCenterArticle","parameters":[{"name":"articleId","required":true,"in":"query","description":"Gleap article identifier from the search response.","schema":{"example":"65f8187b4cc7fe3ddf0f1a3a","type":"string"}},{"name":"helpcenterCollectionId","required":true,"in":"query","description":"Gleap help center collection identifier from the search response.","schema":{"example":"65f818364cc7fe3ddf0f195a","type":"string"}},{"name":"language","required":false,"in":"query","description":"Preferred response language. Defaults to `de`.","schema":{"enum":["de","en"],"type":"string"}}],"responses":{"200":{"description":"Full help center article content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpCenterArticleResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"500":{"description":"Unexpected help center integration response"},"502":{"description":"Gleap help center request failed upstream"},"503":{"description":"Gleap help center integration is not configured on this server"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get help center article content","tags":["help-center","common-lists"],"x-required-scopes":["api:read"]}},"/administration/project-components/item-details/{itemId}/questions":{"post":{"description":"Creates a new question for a component library item or updates an existing question if `id` is provided in the request body.\n\n**What are Questions?**\nQuestions capture customer-specific requirements or project details in a structured way. They make work packages flexible and reusable by allowing later individualization in projects.\n\n**Question Types:**\n- **ShortText**: Single-line text input (name, title, keyword)\n- **Editor**: Formatted multi-line text (descriptions, free text)\n- **Checkbox**: Yes/No selection (can impact effort calculation)\n- **Radio**: Single choice from multiple options (can impact effort)\n- **Files**: Upload files (logos, documents, graphics)\n- **Datepicker**: Select a date (deadlines, delivery dates)\n- **Multiplier**: Dynamic calculation based on quantity (e.g., \"How many pages?\" × duration per page = total effort)\n- **Person**: Select person(s) from project team or clients\n\n**Key Features:**\n- Answers can automatically affect effort calculation\n- Questions can have conditions (shown only if other questions answered a certain way)\n- Multiplier questions calculate: Answer × Duration per element = Total additional effort\n- In library components, questions are defined but not answered\n\nFor Checkbox/Radio types, at least one option must be provided. Description field accepts HTML or Markdown input and returns HTML format. Only library component items (component.projectId is null) are accessible through this endpoint.","operationId":"ComponentItemQuestionsController_createOrUpdateQuestion","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQuestionDto"}}}},"responses":{"200":{"description":"Question created or updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"id":{"type":"string","example":"e57dc37b-7693-4d06-b49c-17084b773aff"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update a question for a library component item","tags":["administration","project-components"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/questions/{questionId}":{"patch":{"description":"Updates specific fields of an existing question. Only provided fields will be updated.\n\n**What are Questions?**\nQuestions capture customer-specific requirements or project details. They can have different types (ShortText, Editor, Checkbox, Radio, Files, Datepicker, Multiplier, Person) and can affect effort calculation.\n\nFor Checkbox/Radio types, if options are provided, at least one option must be included. Description field accepts HTML or Markdown input and returns HTML format. Only library component item questions (component.projectId is null) are accessible through this endpoint.","operationId":"ComponentItemQuestionsController_updateQuestion","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"questionId","required":true,"in":"path","description":"Question UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateQuestionDto"}}}},"responses":{"200":{"description":"Question updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a question for a library component item","tags":["administration","project-components"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a question from a component library item by setting its deletedAt timestamp.\n\n**What are Questions?**\nQuestions capture customer-specific requirements or project details. They are attached to component items (Work Packages, Todo Lists, Test Suites) and can affect effort calculation when answered in projects.\n\nOnly library component item questions (component.projectId is null) are accessible through this endpoint.","operationId":"ComponentItemQuestionsController_deleteQuestion","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"questionId","required":true,"in":"path","description":"Question UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"Question deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a question from a library component item","tags":["administration","project-components"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/questions/sort":{"post":{"description":"Reorders questions within a component library item by providing a new sort order array of question IDs.\n\n**What are Questions?**\nQuestions capture customer-specific requirements or project details. They are attached to component items and displayed in the order specified by their sort field.\n\nAll question IDs in the newSortOrder array must belong to the specified item. Only library component items (component.projectId is null) are accessible through this endpoint.","operationId":"ComponentItemQuestionsController_sortQuestions","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortQuestionsDto"}}}},"responses":{"200":{"description":"Questions reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder questions within a library component item","tags":["administration","project-components"],"x-required-scopes":["api:write"]}},"/administration/project-components/components":{"post":{"description":"Creates a new library component (reusable workspace template) with name, description, icon, tags, and internal notes.\n\n**What are Library Components?**\nLibrary components are workspace-wide reusable templates (projectId is null). They serve as blueprints that can be imported into projects to standardize recurring project types. Components connect requirements management, cost calculation, and execution in one unified workflow.\n\n**After Creating a Component:**\nOnce created, you can add items (Epics, Work Packages, Todo Lists, Test Suites) to define the component structure. You can also add questions to capture customer requirements, todos for checklists, and test cases for acceptance testing.\n\n**Component Placement:**\nThe component is automatically appended to the end of the library component list. You can reorder components later using the sort endpoint.\n\n**Content Format:**\nDescription and internal note fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage. When retrieved, this content is converted back to HTML for easy consumption.","operationId":"ComponentLibraryController_createComponent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComponentDto"}}}},"responses":{"201":{"description":"Component created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"e57dc37b-7693-4d06-b49c-17084b773aff"}}}}}},"400":{"description":"Invalid request: missing required fields or invalid UUID format for tags"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new component library component","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/items":{"post":{"description":"Creates a new component item within a library component. Items are the building blocks that structure component templates.\n\n**What are Component Items?**\nItems are the building blocks within components that organize and structure project work. Each item can be one of four types:\n\n1. **Epic**: Major sections or phases that group related work packages around a common theme or goal\n   - Example: \"Technical Implementation\", \"Design & Development\", \"Project Kickoff & Preparation\"\n   - Can contain Work Packages, Todo Lists, or Test Suites\n   - Can nest other Epics to create complex hierarchies (arbitrary nesting depth)\n   - TimeFrame is not required for Epics\n\n2. **Work Package**: Self-contained, clearly defined tasks that deliver tangible results\n   - Example: \"Set up domain and hosting\", \"Design homepage layout\", \"Configure CMS\"\n   - Can have timeframes/estimates (required field)\n   - Can contain Questions (to capture customer requirements), Todos (checklist items), and Test Cases (acceptance test steps)\n   - Can nest other Work Packages for complex tasks\n\n3. **Todo List**: Checklists of sub-steps or checkpoints\n   - Example: \"Kickoff Checklist\", \"Quality Control Checklist\", \"Go-live Approval\"\n   - Perfect for recurring processes (tests, approvals, installation steps)\n   - Each todo item within the list can be checked off individually\n   - TimeFrame is required\n\n4. **Test Suite**: Formal acceptance tests for project results\n   - Example: \"Contact Form Tests\", \"Responsive Design Check\", \"SEO Review\"\n   - Contains multiple test cases with steps and expected results\n   - TimeFrame is required\n\n**Item Placement:**\nItems can be created at the root level of a component or nested under parent items (Epics or other Work Packages).\n\n**Content Format:**\nThe description and internalNote fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage.\n\n**Validation Rules:**\nTimeFrame is required for all item types except Epic. Only library component items (component.projectId is null) can be created through this endpoint.","operationId":"ComponentLibraryController_createItem","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLibraryComponentItemDto"}}}},"responses":{"201":{"description":"Component item created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLibraryComponentItemResponseDto"}}}},"400":{"description":"Invalid request: invalid UUID format, missing required fields, or validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Component not found or does not belong to library (has projectId)"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new component item (Epic, Work Package, Todo List, or Test Suite)","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/library-tree":{"get":{"description":"Returns the complete hierarchical tree structure of the component library, including all components and their items.\n\n**What is the Component Library?**\nThe component library is a workspace-wide catalog of reusable component templates. These templates can be imported into projects to standardize recurring project types.\n\n**Library Components vs Project Components:**\n- **Library Components** (this endpoint): Workspace-wide templates (projectId is null) - reusable blueprints with no live data\n- **Project Components**: Live instances in projects (projectId is set) - contain answers, evaluations, and can be connected to tasks\n\n**Component Structure:**\n- **Components**: Top-level containers (e.g., \"Website Development\", \"CRM Onboarding\")\n- **Items**: Can be Epics, Work Packages, Todo Lists, or Test Suites\n- Items can be nested to any depth\n\nThis endpoint provides read-only access to the workspace component library for programmatic access.","operationId":"ComponentLibraryController_getLibraryTree","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentLibraryTreeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get component library tree structure","tags":["administration"],"x-required-scopes":["api:read"]}},"/administration/project-components/componentsDetails/{id}":{"get":{"description":"Returns full details of a component library component including all nested children and metadata.\n\n**What are Library Components?**\nLibrary components are workspace-wide reusable templates (projectId is null). They serve as blueprints that can be imported into projects. Unlike project components, library components contain no live data - no answers to questions, no task connections, and no evaluations.\n\nAll ProseMirror editor content (description, internalNote) is converted to HTML format. Only library components (projectId is null) are accessible through this endpoint.","operationId":"ComponentLibraryController_getComponentDetails","parameters":[{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentDetailsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get component library component details","tags":["administration"],"x-required-scopes":["api:read"]},"delete":{"description":"Soft deletes a library component by setting the deletedAt timestamp. The component is marked as deleted but not permanently removed from the database. Automatically cleans up invalid condition targets and updates timeframes.\n\n**What are Library Components?**\nLibrary components are workspace-wide reusable templates (projectId is null). They serve as blueprints that can be imported into projects. Only library components (projectId is null) can be deleted through this endpoint.","operationId":"ComponentLibraryController_deleteComponent","parameters":[{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"Component deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid component ID format (not valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Component not found, component does not belong to library (has projectId), or component already deleted"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete (soft delete) a component library component","tags":["administration"],"x-required-scopes":["api:write"]},"patch":{"description":"Updates only the provided fields of an existing library component (name, description, icon, tags, internalNote). The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. Only library components (projectId is null) can be updated through this endpoint.","operationId":"ComponentLibraryController_patchComponent","parameters":[{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchComponentDto"}}}},"responses":{"200":{"description":"Component updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"e57dc37b-7693-4d06-b49c-17084b773aff"}}}}}},"400":{"description":"Invalid request: invalid UUID format for component ID or tag IDs"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Component not found, component does not belong to library (has projectId), or component already deleted"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a component library component","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{id}":{"get":{"description":"Returns full details of a component library item including all test cases, todos, questions, conditions, and nested children.\n\n**What are Component Items?**\nItems are the building blocks within components. Each item can be:\n- **Epic**: Major section or phase (e.g., \"Technical Implementation\", \"Design & Development\")\n- **Work Package**: Specific task with deliverables (e.g., \"Set up hosting\", \"Design homepage\")\n- **Todo List**: Checklist of actionable items (e.g., \"Kickoff Checklist\", \"Quality Control\")\n- **Test Suite**: Acceptance tests for formal approval (e.g., \"Contact Form Tests\", \"SEO Review\")\n\n**Attached Resources:**\n- **Questions**: Capture customer requirements (defined but not answered in library)\n- **Todos**: Checklist items within a Work Package or Todo List\n- **Test Cases**: Individual test steps within a Test Suite\n\nAll ProseMirror editor content is converted to HTML format. Only library component items (component.projectId is null) are accessible through this endpoint.","operationId":"ComponentLibraryController_getItemDetails","parameters":[{"name":"id","required":true,"in":"path","description":"Component item UUID","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentItemDetailsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get component library item details","tags":["administration"],"x-required-scopes":["api:read"]}},"/administration/project-components/item-details/{itemId}/todos":{"post":{"description":"Creates a new todo checklist item for a library component item.\n\n**What are Todos?**\nTodos are checklist items used to track actionable tasks within component items. They are perfect for:\n- Recurring processes (tests, approvals, installation steps)\n- Quality control checkpoints\n- Project preparation steps\n- Final checks before go-live\n\nEach todo can be checked off individually, supports comments, and tracks completion status. Todos can be attached to Work Packages or Todo List items.\n\nDescription accepts HTML or Markdown format and is converted to IDoc for storage. Only library component items (component.projectId is null) are accessible.","operationId":"ComponentLibraryController_createTodo","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComponentTodoDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoOperationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new todo (checklist item) for a library component item","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/todos/{todoId}":{"patch":{"description":"Updates title and/or description of an existing todo item. Description accepts HTML or Markdown format and is converted to IDoc for storage. Only todos belonging to library component items are accessible.","operationId":"ComponentLibraryController_updateTodo","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"todoId","required":true,"in":"path","description":"Todo UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440011","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateComponentTodoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoOperationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing todo","tags":["administration"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a todo item by setting its deletedAt timestamp. Only todos belonging to library component items are accessible.","operationId":"ComponentLibraryController_deleteTodo","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"todoId","required":true,"in":"path","description":"Todo UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440011","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoOperationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a todo (soft delete)","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/todos/sort":{"post":{"description":"Updates the sort order of todos within a library component item. All todo IDs in newSortOrder must belong to the specified item. Only library component items are accessible.","operationId":"ComponentLibraryController_sortTodos","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortComponentTodosDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoOperationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder todos within an item","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/testcases":{"post":{"description":"Creates a new test case for a library component item.\n\n**What are Test Cases?**\nTest cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results.\n\n**Test Case Structure:**\n- **Title**: Short, clear title (e.g., \"Check confirmation email destination\")\n- **Description**: What is being tested and why it matters\n- **Steps**: Step-by-step instructions for running the test\n- **Expected Result**: Describes the expected or correct system behavior\n\n**Evaluation:**\nTest cases can be evaluated as Passed, Failed, or PassedWithReservations. The project is considered finished when all test cases have been passed successfully.\n\nEditor content (description, steps, expectedResult) accepts HTML or Markdown input and returns HTML output. Only library component items (component.projectId is null) can have testcases created.","operationId":"ComponentLibraryController_createTestCase","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComponentTestCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentTestCaseResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new test case (acceptance test step) for a library component item","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/testcases/{testCaseId}":{"patch":{"description":"Updates an existing test case for a library component item. All fields are optional for partial updates. Editor content (description, steps, expectedResult) accepts HTML or Markdown input and returns HTML output. Only library component test cases can be updated.","operationId":"ComponentLibraryController_updateTestCase","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"testCaseId","required":true,"in":"path","description":"Test case UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440010","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateComponentTestCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentTestCaseResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing testcase","tags":["administration"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a test case from a library component item (soft delete - sets deletedAt timestamp). Only library component test cases can be deleted.","operationId":"ComponentLibraryController_deleteTestCase","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"testCaseId","required":true,"in":"path","description":"Test case UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440010","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a testcase (soft delete)","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/item-details/{itemId}/testcases/sort":{"post":{"description":"Updates the sort order of test cases within a library component item. Provide an array of testcase IDs in the desired order. Only library component test cases can be reordered.","operationId":"ComponentLibraryController_sortTestCases","parameters":[{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortComponentTestCasesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder testcases within an item","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/items/{id}":{"patch":{"description":"Updates only the provided fields of an existing component library item (epic, work package, todo list, test suite). The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. When the conditions array is provided, it completely replaces existing conditions. Conditions with id are updated, conditions without id are created, and conditions not in the array are deleted. Only library component items (component.projectId is null) can be updated through this endpoint.","operationId":"ComponentLibraryController_patchItem","parameters":[{"name":"id","required":true,"in":"path","description":"Component item UUID","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchLibraryComponentItemDto"}}}},"responses":{"200":{"description":"Item updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3047983b-d798-4aff-8dd4-628cf1900703"}}}}}},"400":{"description":"Invalid request: invalid UUID format for item ID, component ID, tag IDs, or invalid type/timeFrame combination"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Item not found, item does not belong to library component, or component not found"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a component library item","tags":["administration"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a component library item by setting the deletedAt timestamp. The item is marked as deleted but not permanently removed from the database. Automatically cleans up invalid condition targets and updates timeframes. Only library component items (component.projectId is null) can be deleted through this endpoint.","operationId":"ComponentLibraryController_deleteItem","parameters":[{"name":"id","required":true,"in":"path","description":"Component item UUID","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"responses":{"200":{"description":"Item deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteComponentItemResponseDto"}}}},"400":{"description":"Invalid component item ID format (not valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Item not found, item does not belong to library component, or item already deleted"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete (soft delete) a component library item","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/tree/sort":{"post":{"description":"Allows reordering components and items at the same level, reparenting items to different components or parent items, and moving items between components (automatically updates componentId for all descendants). When parent is null, sorts components. When parent is provided, sorts items within that parent. Automatically handles side-effects including conditions cleanup and timeframe updates. Only library components and items (component.projectId is null) can be reordered through this endpoint.","operationId":"ComponentLibraryController_saveComponentsSort","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveComponentsSortDto"}}}},"responses":{"200":{"description":"Tree structure updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Products sort updated successfully"}}}}}},"400":{"description":"Invalid request format, invalid UUIDs, or invalid type values"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Component/item not found in library"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Move/reorder items in component library tree","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/components/{id}/duplicate":{"post":{"description":"Creates a copy of an existing library component including all nested data (items, questions, todos, test cases, and conditions). The duplicated component is automatically named with \"(Copy)\" suffix and placed immediately after the original component in the sort order. Condition references are automatically updated to point to the new duplicated items. Recalculates conditions and timeframes. Only library components (projectId is null) can be duplicated through this endpoint.","operationId":"ComponentLibraryController_duplicateComponent","parameters":[{"name":"id","required":true,"in":"path","description":"Component UUID to duplicate","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"201":{"description":"Component duplicated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateComponentResponseDto"}}}},"400":{"description":"Invalid component ID format (not a valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Component not found or does not belong to library (has projectId)"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Duplicate a component within the library","tags":["administration"],"x-required-scopes":["api:write"]}},"/administration/project-components/components/{id}/recalculate-estimates":{"post":{"description":"Recalculates time estimates for all items (epics and non-epics) within a library component. This includes:\n- Recalculating individual item estimates based on timeFrame and question multipliers/extra hours\n- Recalculating epic estimates by summing their children's estimates\n- Updating the component's totalTimeFrame\n- Respecting visibility conditions (items with invalid conditions are excluded)\n\nItems are processed bottom-up (children first, then parents) to ensure accurate calculations. Only library components (projectId is null) can be recalculated through this endpoint.","operationId":"ComponentLibraryController_recalculateComponentEstimates","parameters":[{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"Estimates recalculated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid component ID format (not a valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to edit component library"},"404":{"description":"Component not found or does not belong to library (has projectId)"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Recalculate all estimates for a library component","tags":["administration"],"x-required-scopes":["api:write"]}},"/tags/{type}/list":{"get":{"description":"Retrieves all tags for the specified type (task, project, or project-component) in the workspace.\n\n**What are Tags?**\nTags in Leadtime enable cross-project organization, visual marking, and filtering of tasks, projects, and project components by topics or processes. They help you link related work across different projects and quickly find all entries with a specific tag.\n\n**Tag Types:**\n- **Task tags**: Used to label and organize individual tasks/tickets across all projects\n- **Project tags**: Used to categorize and organize entire projects\n- **Project component tags**: Used in project component library to label work packages, checklists, and test suites. These tags are automatically inherited by all elements underneath (cascading tags), making it easy to organize tasks that belong together.\n\n**What is returned:**\nReturns an array of tag objects, each containing:\n- Tag ID (UUID)\n- Tag name\n- Tag color (predefined color code, optional)\n- Deletion status (soft-deleted tags are marked but not removed)\n\n**Permission requirements:**\n- Task tags: No permission required (accessible to all authenticated users)\n- Project tags: No permission required (accessible to all authenticated users)\n- Project component tags: Requires ProjectComponentsLibrary.view OR Projects.manageComponents permission","operationId":"TagsController_list","parameters":[{"name":"type","required":true,"in":"path","description":"The type of tags to work with. Determines which tag category you are accessing:\n- \"task\": Tags for individual tasks/tickets across all projects\n- \"project\": Tags for categorizing entire projects\n- \"project-component\": Tags used in project component library (with cascading inheritance)\n- \"organization\": Tags for categorizing organizations\n- \"object\": Tags for workspace object instances\n- \"form-template\": Tags for workspace form templates (administration)","schema":{"example":"task","type":"string","enum":["task","project","project-component","organization","object","form-template"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagResponseDto"}}}}},"400":{"description":"Invalid tag type"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all tags of specified type","tags":["tags","common-lists"],"x-required-scopes":["api:read"]}},"/tags/{type}/create":{"post":{"description":"Creates a new tag of the specified type. If a tag with the same name already exists (case-insensitive), returns the existing tag instead of creating a duplicate.\n\n**How tag creation works:**\n- Provide a tag name (required)\n- The system checks for existing tags with the same name (case-insensitive)\n- If a tag exists, it is returned (no duplicate created)\n- If no tag exists, a new one is created\n- Tag color can be set later using the save endpoint\n\n**Tag Colors:**\nTags use predefined color codes, not hex values. Available colors: `default`, `red`, `yellow`, `green`, `blue`, `purple`, `fuchsia`, `pink`, `magenta`.\n\n**Use cases:**\n- Create standard tags for recurring topics (e.g., \"Billing\", \"Support\", \"Onboarding\")\n- Organize tasks by process or workflow stage\n- Enable cross-project filtering and analysis\n- Set up cascading tags in project components for automatic inheritance\n\n**Best practices:**\n- Use consistent naming to avoid duplicates (e.g., do not use both \"Billing\" and \"Invoice\")\n- Create standard tags for recurring topics to keep your team organized\n- Use colors strategically for visual organization (set via save endpoint)\n\n**Permission requirements:**\n- Task tags: Requires Tasks.createTags permission\n- Project tags: Must be an employee (not organization member)\n- Project component tags: Requires ProjectComponentsLibrary.edit OR Projects.manageComponents permission","operationId":"TagsController_create","parameters":[{"name":"type","required":true,"in":"path","description":"The type of tags to work with. Determines which tag category you are accessing:\n- \"task\": Tags for individual tasks/tickets across all projects\n- \"project\": Tags for categorizing entire projects\n- \"project-component\": Tags used in project component library (with cascading inheritance)\n- \"organization\": Tags for categorizing organizations\n- \"object\": Tags for workspace object instances\n- \"form-template\": Tags for workspace form templates (administration)","schema":{"example":"task","type":"string","enum":["task","project","project-component","organization","object","form-template"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponseDto"}}}},"400":{"description":"Invalid tag type or duplicate name"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new tag","tags":["tags","common-lists"],"x-required-scopes":["api:write"]}},"/tags/{type}/save":{"post":{"description":"Updates an existing tag name and color. This endpoint allows you to rename tags or change their visual appearance.\n\n**What can be updated:**\n- **Tag name**: Change the tag label (must be unique within the tag type, case-insensitive)\n- **Tag color**: Set or change the predefined color code for visual organization\n\n**How it works:**\n- Provide the tag ID and new name (required)\n- Optionally provide a predefined color code (e.g., \"red\", \"blue\", \"green\")\n- The system validates that the new name does not conflict with existing tags\n- The system validates that the color is one of the predefined values\n- All entities using this tag will automatically reflect the changes\n\n**Tag Colors:**\nTags use predefined color codes, not hex values. Available colors:\n- `default` - Light gray (default color)\n- `red` - Red\n- `yellow` - Yellow\n- `green` - Green\n- `blue` - Blue\n- `purple` - Purple\n- `fuchsia` - Fuchsia\n- `pink` - Pink\n- `magenta` - Magenta\n\n**Use cases:**\n- Rename tags for better consistency\n- Add or change colors for visual organization\n- Standardize tag naming across the workspace\n\n**Color strategy tips:**\n- Use green for active processes\n- Use blue for customer-related items\n- Use red for issues or urgent items\n\n**Permission requirements:**\n- Task tags: Requires Tasks.manageTags permission\n- Project tags: Must be an employee (not organization member)\n- Project component tags: Requires ProjectComponentsLibrary.edit OR Projects.manageComponents permission","operationId":"TagsController_save","parameters":[{"name":"type","required":true,"in":"path","description":"The type of tags to work with. Determines which tag category you are accessing:\n- \"task\": Tags for individual tasks/tickets across all projects\n- \"project\": Tags for categorizing entire projects\n- \"project-component\": Tags used in project component library (with cascading inheritance)\n- \"organization\": Tags for categorizing organizations\n- \"object\": Tags for workspace object instances\n- \"form-template\": Tags for workspace form templates (administration)","schema":{"example":"task","type":"string","enum":["task","project","project-component","organization","object","form-template"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveTagDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponseDto"}}}},"400":{"description":"Invalid tag type, duplicate name, or invalid color value"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tag not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing tag","tags":["tags","common-lists"],"x-required-scopes":["api:write"]}},"/tags/{type}/{id}":{"delete":{"description":"Soft deletes a tag and automatically removes it from all associated entities (tasks, projects, or components). The tag is marked as deleted but not permanently removed from the database.\n\n**How soft deletion works:**\n- The tag is marked as deleted (isDeleted flag set to true)\n- The tag is automatically removed from all entities that were using it\n- Tasks, projects, or components will no longer show this tag\n- The tag will not appear in list endpoints\n- Historical data is preserved (the tag still exists in the database)\n\n**Use cases:**\n- Remove tags that are no longer needed\n- Clean up duplicate or incorrectly named tags\n- Archive tags that are no longer in use\n\n**Note:** Soft deletion means the tag can potentially be restored if needed, but this endpoint does not provide restoration functionality.\n\n**Permission requirements:**\n- Task tags: Requires Tasks.manageTags permission\n- Project tags: Must be an employee (not organization member)\n- Project component tags: Requires ProjectComponentsLibrary.edit OR Projects.manageComponents permission","operationId":"TagsController__delete","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponseDto"}}}},"400":{"description":"Invalid tag type"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tag not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Soft delete a tag","tags":["tags","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/form-instances":{"get":{"description":"Returns every form instance on the task, including `templateId`, current `values`, and a `fields` array for each instance. Each field includes `valueKey` — use that string as the property name in the `values` object when calling PATCH `/tasks/{identifier}/form-instances/{formInstanceId}`. To choose which template to attach next, list templates with GET `/workspace/form-templates` (template `id` is the `templateId` for POST). For the full static schema of a template (before attach), use GET `/workspace/form-templates/{id}`.","operationId":"TaskFormInstancesController_list","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or shortNumber","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List form instances on a task","tags":["tasks","form-instances"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new form instance from a workspace template. Pass `templateId` from GET `/workspace/form-templates` (each item includes `id`) or the legacy GET `/administration/form-templates-available`. To review fields, labels, and validation constraints before attaching, call GET `/workspace/form-templates/{id}` with that template id. After attach, fill values with PATCH `/tasks/{identifier}/form-instances/{formInstanceId}` (get `formInstanceId` from GET this resource list).","operationId":"TaskFormInstancesController_attach","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or shortNumber","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachTaskFormInstanceDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Attach a form template to a task","tags":["tasks","form-instances"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/form-instances/{formInstanceId}":{"patch":{"description":"Merges the provided `values` into the instance (see request body schema). Keys in `values` must match `fields[].valueKey` from GET `/tasks/{identifier}/form-instances` for this instance. For allowed types and select options, either read `fields` on that GET response or load the template definition with GET `/workspace/form-templates/{templateId}` using the instance `templateId`.","operationId":"TaskFormInstancesController_patchValues","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or shortNumber","schema":{"type":"string"}},{"name":"formInstanceId","required":true,"in":"path","description":"Form instance UUID from GET `/tasks/{identifier}/form-instances` (`id` on each item).","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTaskFormInstanceDto"}}}},"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update stored values for a form instance","tags":["tasks","form-instances"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes the instance from the task. Use `formInstanceId` from GET `/tasks/{identifier}/form-instances`. To attach again later, pick a template id from GET `/workspace/form-templates`.","operationId":"TaskFormInstancesController_remove","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or shortNumber","schema":{"type":"string"}},{"name":"formInstanceId","required":true,"in":"path","description":"Form instance UUID from GET `/tasks/{identifier}/form-instances`.","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Remove a form instance from a task","tags":["tasks","form-instances"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/agent-sessions":{"get":{"operationId":"TaskAgentSessionsController_listTaskSessions","parameters":[{"name":"identifier","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"bearer":[]},{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List agent sessions for a task","tags":["agent-sessions","tasks"],"x-required-scopes":["api:read"]}},"/tasks/{identifier}/products":{"post":{"description":"**What are Task Products?**\nTask products allow you to add standardized services or goods from your product catalog directly to tasks (tickets). This enables quick pricing for support requests, service tickets, or express quotes without creating a full project. Products can include variants (e.g., Standard, Pro, Enterprise) and options (e.g., additional services or upgrades).\n\n**How this endpoint works:**\nThis endpoint imports a product from your workspace product catalog into a specific task. The product is copied into the task context, allowing you to customize quantity, select variants, and configure options without affecting the original catalog product.\n\n**What you need to provide:**\n- **productId**: The UUID of the product from your catalog (must exist in catalog, not already assigned to a task or project)\n- **quantity**: How many units of this product (minimum 1)\n- **activeVariantId**: (Optional) Which variant to activate if the product has multiple variants\n- **options**: Array of option selections, where each option specifies which values are selected\n\n**Important notes:**\n- Only products from the catalog (not already assigned to tasks or projects) can be imported\n- The product is copied into the task - changes here do not affect the catalog version\n- Products support three pricing models: fixed price, subscription price, and per-unit pricing\n- Variants allow different configurations (e.g., different package sizes or service levels)\n- Options allow customers to add extra services or upgrades (e.g., \"Add workshop\" or \"Enable premium support\")\n\n**What is returned:**\nReturns the ID of the newly created task product, which can be used for subsequent updates or deletion.\n\n**Permission requirements:**\nRequires \"manageProducts\" permission on tasks and \"api:write\" scope.","operationId":"TaskProductsController_importProductToTask","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task identifier. Can be either a UUID (e.g., \"550e8400-e29b-41d4-a716-446655440000\") or a numeric shortNumber (e.g., \"123\"). The shortNumber is a human-readable task number assigned by the system.","schema":{"example":"123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportProductToTaskDto"}}}},"responses":{"201":{"description":"Product imported successfully","schema":{"example":{"success":true,"id":"550e8400-e29b-41d4-a716-446655440000"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad Request - Invalid input or validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no task access"},"404":{"description":"Not Found - Catalog product or task does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Import product from catalog to task","tags":["tasks","products"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/products/{productId}":{"patch":{"description":"**What this endpoint does:**\nUpdates an existing product that has been imported into a task. This endpoint supports partial updates - you only need to provide the fields you want to change. All other fields remain unchanged.\n\n**What can be updated:**\n- **Basic information**: Name, description (HTML or Markdown), category, logo\n- **Pricing**: Fixed price, subscription price, per-unit price, unit name, billing frequency\n- **Variants**: Add, update, or modify product variants (e.g., Standard, Pro, Enterprise versions)\n- **Options**: Modify product options and their values (e.g., additional services, upgrades)\n\n**Important notes:**\n- This is a PATCH operation - only provided fields are updated\n- Description can be provided as HTML or Markdown - it will be automatically converted to the internal document format\n- Variant descriptions also support HTML or Markdown\n- When updating variants, you must provide the complete variant structure (existing variants are replaced)\n- When updating options, you must provide the complete options structure (existing options are replaced)\n- Changes only affect the product within this task - the catalog product remains unchanged\n- Product must belong to the specified task\n\n**Pricing models:**\n- **Fixed price**: One-time payment amount\n- **Subscription price**: Recurring payment (e.g., monthly, yearly)\n- **Price per unit**: Calculated per quantity (e.g., per user, per hour)\n- **Price frequency**: Billing interval for subscriptions (in months)\n\n**What is returned:**\nReturns success status and the product ID.\n\n**Permission requirements:**\nRequires \"manageProducts\" permission on tasks and \"api:write\" scope.","operationId":"TaskProductsController_patchProduct","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task identifier. Can be either a UUID (e.g., \"550e8400-e29b-41d4-a716-446655440000\") or a numeric shortNumber (e.g., \"123\"). The shortNumber is a human-readable task number assigned by the system.","schema":{"example":"123","type":"string"}},{"name":"productId","required":true,"in":"path","description":"UUID of the product that was imported into the task. This is the ID returned when importing a product via the POST endpoint.","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTaskProductDto"}}}},"responses":{"200":{"description":"Product updated successfully","schema":{"example":{"success":true,"id":"550e8400-e29b-41d4-a716-446655440000"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad Request - Invalid input or validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no task access"},"404":{"description":"Not Found - Product or task does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update task product","tags":["tasks","products"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this endpoint does:**\nRemoves a product from a task. This performs a soft delete - the product is marked as deleted but not permanently removed from the database. This preserves historical data for auditing and reporting purposes.\n\n**Important notes:**\n- Product must belong to the specified task\n- This is a soft delete - the product record remains in the database with deletedAt timestamp set\n- Deleted products are excluded from normal queries but can be accessed for historical purposes\n- The original catalog product is not affected by this deletion\n- Once deleted, the product cannot be restored through this API (requires database-level operation)\n\n**What is returned:**\nReturns success status.\n\n**Permission requirements:**\nRequires \"manageProducts\" permission on tasks and \"api:write\" scope.","operationId":"TaskProductsController_deleteProduct","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task identifier. Can be either a UUID (e.g., \"550e8400-e29b-41d4-a716-446655440000\") or a numeric shortNumber (e.g., \"123\"). The shortNumber is a human-readable task number assigned by the system.","schema":{"example":"123","type":"string"}},{"name":"productId","required":true,"in":"path","description":"UUID of the product that was imported into the task. This is the ID returned when importing a product via the POST endpoint.","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Product deleted successfully","schema":{"example":{"success":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no task access"},"404":{"description":"Not Found - Product or task does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete task product","tags":["tasks","products"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/products/{productId}/settings":{"post":{"description":"**What this endpoint does:**\nUpdates the configuration settings for a product that has been imported into a task. This includes quantity, which variant is active, and which option values are selected. This is different from updating the product details (name, description, prices) - use PATCH for those changes.\n\n**What can be configured:**\n- **quantity**: How many units of this product (minimum 1)\n- **activeVariantId**: Which variant is currently active (if product has variants)\n- **options**: Which option values are selected for each product option\n\n**Use cases:**\n- Adjust quantity after initial import\n- Switch between variants (e.g., from Standard to Pro)\n- Change option selections (e.g., add or remove premium support)\n- Configure product for specific customer requirements\n\n**Important notes:**\n- This endpoint only updates configuration, not product details (name, description, prices)\n- To update product details, use the PATCH endpoint instead\n- Product must belong to the specified task\n- All option selections must be provided (partial updates not supported for options)\n\n**What is returned:**\nReturns success status.\n\n**Permission requirements:**\nRequires \"manageProducts\" permission on tasks and \"api:write\" scope.","operationId":"TaskProductsController_updateProductSettings","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task identifier. Can be either a UUID (e.g., \"550e8400-e29b-41d4-a716-446655440000\") or a numeric shortNumber (e.g., \"123\"). The shortNumber is a human-readable task number assigned by the system.","schema":{"example":"123","type":"string"}},{"name":"productId","required":true,"in":"path","description":"UUID of the product that was imported into the task. This is the ID returned when importing a product via the POST endpoint.","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskProductSettingsDto"}}}},"responses":{"200":{"description":"Product settings updated successfully","schema":{"example":{"success":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad Request - Invalid input or validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no task access"},"404":{"description":"Not Found - Product or task does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task product settings","tags":["tasks","products"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/express-quotations":{"post":{"description":"Creates a new express quotation for the specified task. Express quotations are pricing estimates that can be sent to contacts and, when accepted, replace time-based billing with the quotation amount.\n\n**What are Express Quotations?**\nExpress quotations provide a way to offer fixed-price estimates for tasks instead of billing by time. They:\n- Link pricing estimates directly to tasks\n- Can be sent to contacts via email\n- When accepted, override time-based billing for the task\n- Include line items with quantities, unit prices, and options\n- Support tax calculations and multi-language content\n\n**How items are generated:**\n- Items are automatically created from task products (if any)\n- If `includeEstimate` is true, estimated work time is added as the first item\n- Each item can have options (variants) with their own pricing\n- Prices, quantities, and totals are calculated automatically\n\n**What is returned:**\n- Complete quotation with all items and options\n- Calculated subtotal, tax amount, and total\n- HTML-formatted comment and item descriptions\n- Quotation status (initially Pending)\n- Contact user and language information\n\n**Note:** The task identifier can be either a UUID or numeric shortNumber. The quotation is automatically assigned a sequential shortNumber per task.","operationId":"ExpressQuotationsController_create","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExpressQuotationDto"}}}},"responses":{"201":{"description":"Express quotation created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpressQuotationResponseDto"}}}},"400":{"description":"Invalid input data or task identifier"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create express quotation","tags":["tasks","express-quotations"],"x-required-scopes":["api:write"]},"get":{"description":"Returns all express quotations for the specified task, ordered by creation date (newest first).\n\n**What is returned:**\n- All non-deleted quotations for the task\n- Each quotation includes:\n  - Full item list with options\n  - Pricing details (subtotal, tax, total)\n  - Status (Pending, Accepted, or Rejected)\n  - Contact user and language\n  - HTML-formatted comments and descriptions\n  - Creation, update, and sent timestamps\n\n**Quotation statuses:**\n- **Pending**: Quotation created but not yet accepted or rejected\n- **Accepted**: Quotation accepted, will be used for billing instead of time\n- **Rejected**: Quotation rejected, will not be used for billing\n\n**Note:** Only one quotation per task can be Accepted at a time. When a quotation is accepted, all other Pending or Accepted quotations for the same task are automatically rejected.","operationId":"ExpressQuotationsController_list","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}}],"responses":{"200":{"description":"Express quotations retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExpressQuotationResponseDto"}}}}},"400":{"description":"Invalid task identifier"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List express quotations","tags":["tasks","express-quotations"],"x-required-scopes":["api:read"]}},"/tasks/{identifier}/express-quotations/{quotationId}":{"get":{"description":"Returns complete details of a specific express quotation, including all items, options, and pricing calculations.\n\n**What is returned:**\n- Quotation metadata (ID, shortNumber, status, timestamps)\n- Contact user and language information\n- HTML-formatted comment\n- All items with:\n  - Title and HTML-formatted description\n  - Unit type (Hour or Piece)\n  - Quantity, unit price, and total price\n  - All options (variants) with their pricing\n- Financial summary:\n  - Subtotal (before tax)\n  - Tax rate and tax amount\n  - Total (after tax)\n- User tracking (created by, status changed by)\n- Email tracking (sentAt timestamp if sent)\n\n**Use cases:**\n- View quotation details before sending\n- Review pricing breakdown\n- Check quotation status and history","operationId":"ExpressQuotationsController_getById","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"quotationId","required":true,"in":"path","description":"Quotation UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Express quotation retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpressQuotationResponseDto"}}}},"400":{"description":"Invalid task identifier or quotation ID"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task or quotation not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get single express quotation","tags":["tasks","express-quotations"],"x-required-scopes":["api:read"]},"delete":{"description":"Soft deletes the quotation by setting the `deletedAt` timestamp. Deleted quotations are excluded from listings and cannot be used for billing.\n\n**What happens when a quotation is deleted:**\n- The quotation is marked as deleted (soft delete)\n- It will not appear in quotation listings\n- It cannot be accepted, rejected, or sent via email\n- If this was the accepted quotation, the task's `hasAcceptedQuotation` flag is cleared\n- Historical data is preserved for audit purposes\n\n**Note:** This is a soft delete operation. The quotation data remains in the database but is hidden from normal operations. Deleted quotations cannot be restored through the API.","operationId":"ExpressQuotationsController__delete","parameters":[{"name":"quotationId","required":true,"in":"path","description":"Quotation UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000"}},{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123"}}],"responses":{"200":{"description":"Quotation deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid task identifier or quotation ID"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task or quotation not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete quotation","tags":["tasks","express-quotations"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/express-quotations/{quotationId}/accept":{"post":{"description":"Accepts the quotation, marking it as the active quotation for billing purposes. This action automatically rejects all other Pending or Accepted quotations for the same task.\n\n**What happens when a quotation is accepted:**\n- The quotation status changes to Accepted\n- All other Pending or Accepted quotations for the same task are automatically rejected\n- The task is marked with `hasAcceptedQuotation = true`\n- The task's `acceptedQuotationSubTotal` is set to this quotation's subtotal\n- When the task is billed, the quotation amount is used instead of time-based billing\n- Notifications are sent to task subscribers\n\n**Billing impact:**\nOnce accepted, the task will use the quotation's subtotal for invoicing instead of calculating billing from `spentTime * hourRate`. The task must still be in Closed or Resolved status and not already billed to be included in invoices.\n\n**Note:** Only one quotation per task can be Accepted at a time. Accepting a new quotation will automatically reject the previously accepted one.","operationId":"ExpressQuotationsController_accept","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"quotationId","required":true,"in":"path","description":"Quotation UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Quotation accepted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid task identifier or quotation ID"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task or quotation not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Accept quotation","tags":["tasks","express-quotations"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/express-quotations/{quotationId}/reject":{"post":{"description":"Rejects the quotation, marking it as not accepted. Rejected quotations will not be used for billing.\n\n**What happens when a quotation is rejected:**\n- The quotation status changes to Rejected\n- The quotation will not be used for task billing\n- If this was the accepted quotation, the task's `hasAcceptedQuotation` flag is cleared\n- Notifications are sent to task subscribers\n\n**Note:** Rejecting a quotation does not affect other quotations for the same task. You can reject multiple quotations, but only one can be Accepted at a time.","operationId":"ExpressQuotationsController_reject","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"quotationId","required":true,"in":"path","description":"Quotation UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Quotation rejected successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid task identifier or quotation ID"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task or quotation not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reject quotation","tags":["tasks","express-quotations"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/express-quotations/{quotationId}/send-email":{"post":{"description":"Sends the quotation as a formatted document via email to the contact user specified in the quotation.\n\n**What is sent:**\n- A formatted quotation document (PDF or HTML email)\n- Includes all items, options, pricing, and tax calculations\n- Contains the quotation comment and task information\n- Formatted according to the quotation's language setting\n\n**Requirements:**\n- The contact user must have a valid email address\n- The quotation must not be deleted\n- Email sending must be configured in the workspace\n\n**What happens:**\n- The quotation's `sentAt` timestamp is updated\n- An email is sent to the contact user's email address\n- The contact user can view and potentially accept/reject the quotation\n\n**Note:** This endpoint only sends the email. To accept or reject a quotation, use the `/accept` or `/reject` endpoints. The contact user may need to log in or use a special link to interact with the quotation.","operationId":"ExpressQuotationsController_sendEmail","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"quotationId","required":true,"in":"path","description":"Quotation UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Quotation email sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid task identifier, quotation ID, or contact user has no email"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"No access to task project"},"404":{"description":"Task or quotation not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Send quotation email","tags":["tasks","express-quotations"],"x-required-scopes":["api:write"]}},"/tasks/bigPicture":{"get":{"description":"**What is Big Picture?**\nBig Picture is a visual overview of all tasks in a project, organized by task type (like Feature, Bug, or Management). It provides a quick way to assess project status, priorities, bottlenecks, and time efforts without diving into individual task details. This view is especially useful for project members who need a high-level understanding of project progress.\n\n**Visual structure:**\nThe Big Picture displays tasks as cards arranged in horizontal rows. Each row represents a task type (e.g., Feature, Bug, Management). Within each row, tasks are sorted from left to right by priority - tasks further to the left have higher priority than those to the right.\n\n**What data is returned:**\nThe response includes:\n- **Groups**: Array of task groups, where each group represents a task type (row). Each group contains:\n  - **Task items (cards)**: Each card represents a single task and includes:\n    - Task status (color-coded: New, In Progress, Feedback, Resolved, Backlog)\n    - Task ID, title, and short description\n    - Priority level and task type\n    - Time information: planned hours vs spent hours (e.g., \"11.8 h / 40 h\")\n    - Due date warnings if applicable\n    - Comment count and pipeline status indicator\n    - Tags and assigned people (responsible person and stakeholders)\n  - **Group statistics**: Metrics for that specific task type:\n    - Number of resolved tasks\n    - Total estimated hours for all tasks in the group\n    - Total spent hours across all tasks in the group\n    - Resolved hours (time spent on completed tasks)\n- **Overall statistics**: Aggregated metrics across all tasks in the project:\n  - Total number of tasks\n  - Number of resolved tasks\n  - Total estimated hours\n  - Total spent hours\n  - Resolved hours\n\n**Task grouping:**\nTasks are grouped by their task type. Tasks without a recognized type are grouped into a special \"other\" category. Groups are sorted according to the configured task type order (which can be changed using the group-order endpoint).\n\n**Use cases:**\n- Quick project status assessment without opening individual tasks\n- Identifying bottlenecks and overload situations by comparing estimated vs spent hours\n- Prioritizing customer requests visually - arrange tasks left to right based on importance\n- Getting an overview when covering for absent team members - see priorities and status at a glance\n- Visual project planning and coordination - understand what needs attention most urgently\n- Communicating priorities to customers - show them which tasks matter most\n\n**Permissions:**\nRequires BigPicture.canView permission and access to the specified project.\n\n**Note:** If you need to retrieve all tasks with custom filtering, sorting, or pagination capabilities, consider using the GET /tasks/grid endpoint instead. The grid endpoint provides more flexible filtering options and is better suited for retrieving large datasets or when you need to filter by multiple criteria simultaneously.","operationId":"TasksBigPictureController_getBigPicture","parameters":[{"name":"projectId","required":true,"in":"query","description":"UUID of the project for which to retrieve Big Picture data. The project must exist, not be deleted, and the user must have access to it.","schema":{"format":"uuid","example":"7225cca9-ac5f-461d-9a9d-ad8d7ee1b597","type":"string"}}],"responses":{"200":{"description":"Big picture data successfully retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BigPictureResponseDto"}}}},"400":{"description":"Invalid project ID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks BigPicture.canView permission or project access"},"404":{"description":"Project not found or deleted"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get big picture data for a project","tags":["tasks","big-picture"],"x-required-scopes":["api:read"]}},"/tasks/bigPicture/order":{"post":{"description":"**What this endpoint does:**\nReorders tasks within a specific task type group (row) in the Big Picture view. Tasks are arranged from left to right, where tasks further to the left have higher priority than those to the right. This is equivalent to dragging and dropping tasks within a row in the visual interface.\n\n**How it works:**\n- Provide the project ID and the group ID (task type ID) you want to reorder\n- Provide an array of task IDs in the desired new order (left to right = highest to lowest priority)\n- The order array must include all tasks currently in that group - you cannot omit tasks\n- Tasks are reordered within their group only; this does not move tasks between groups or change task types\n\n**Visual behavior:**\nAfter reordering, tasks will appear in the Big Picture view in the new sequence. The leftmost task becomes the highest priority item for that task type, making it easy to see what should be worked on first.\n\n**Use cases:**\n- Prioritizing tasks within a category (e.g., which features are most important to customers)\n- Adjusting task order based on customer feedback during meetings\n- Reorganizing tasks after planning discussions or sprint planning\n- Setting up task sequence for team members to follow\n- Responding to urgent requests by moving critical tasks to the left\n\n**Validation:**\n- Project must exist and not be deleted\n- Group ID must be a valid task type ID for the project\n- Order array must contain valid task IDs that belong to the specified group\n- All tasks currently in the group must be included in the order array (no tasks can be omitted)\n\n**Permissions:**\nRequires BigPicture.canEdit permission and access to the specified project.","operationId":"TasksBigPictureController_updateBigPictureOrder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBigPictureOrderDto"}}}},"responses":{"200":{"description":"Tasks successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid project ID, group ID, or order array"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks BigPicture.canEdit permission or project access"},"404":{"description":"Project not found or deleted"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder tasks within a group (task type)","tags":["tasks","big-picture"],"x-required-scopes":["api:write"]}},"/tasks/bigPicture/group-order":{"post":{"description":"**What this endpoint does:**\nReorders the task type groups (rows) themselves in the Big Picture view. This changes which task types appear first, second, third, etc., from top to bottom in the visual display. This is equivalent to dragging and dropping entire rows in the visual interface.\n\n**How it works:**\n- Provide the project ID\n- Provide an array of group IDs (task type IDs) in the desired new order (top to bottom)\n- The order array must include all groups currently in the project - you cannot omit groups\n- This reorders entire rows, not individual tasks within rows\n- The first group ID in the array will appear at the top, the last at the bottom\n\n**Visual behavior:**\nAfter reordering, task type rows will appear in the Big Picture view in the new sequence. This affects the visual hierarchy - task types at the top are more prominent and appear first when viewing the project.\n\n**Use cases:**\n- Prioritizing which task types are most important (e.g., Bugs before Features, urgent categories first)\n- Organizing the view to match team workflow and working habits\n- Customizing the Big Picture layout for different project contexts or customer preferences\n- Improving visual hierarchy for project stakeholders - show most relevant categories first\n- Grouping related task types together for better overview\n\n**Special groups:**\n- Tasks without a recognized type are grouped into a special \"other\" category\n- The \"other\" group can be included in the order array like any other group\n\n**Validation:**\n- Project must exist and not be deleted\n- Order array must contain valid group IDs (task type IDs) that exist in the project\n- All groups currently in the project must be included in the order array (no groups can be omitted)\n\n**Permissions:**\nRequires BigPicture.canEdit permission and access to the specified project.","operationId":"TasksBigPictureController_updateBigPictureGroupOrder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBigPictureGroupOrderDto"}}}},"responses":{"200":{"description":"Groups successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid project ID or order array"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks BigPicture.canEdit permission or project access"},"404":{"description":"Project not found or deleted"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder task type groups (rows)","tags":["tasks","big-picture"],"x-required-scopes":["api:write"]}},"/tasks/pool":{"get":{"description":"The pool view provides a cross-project overview of all tasks filtered by the Accountable field (not the Assigned field). Unlike the Big Picture view which focuses on a single project, the pool aggregates tasks across all projects where the specified user is listed in the Accountable field.\n\n**Important**: This endpoint filters tasks by the Accountable field, not the Assigned field. Only tasks where the accountableId user is set as the Accountable person will appear in the pool results.\n\nThis view is commonly used for:\n- Weekly planning meetings between team leads and account managers\n- Prioritizing tasks across multiple client projects\n- Reviewing completed work and discussing open tickets\n- Managing workload and identifying blockers\n\nThe response organizes tasks by project, with each project containing groups organized by task type (e.g., Feature, Bug, Task). Projects are sorted by custom order stored in PoolProjectOrder table, and groups within projects are sorted by custom order from PoolTypeProjectOrder table.\n\nThe response includes:\n- Overall statistics: total tasks, resolved count, total estimated hours, total spent hours, resolved hours\n- Per-project structure: each project contains groups of tasks organized by type\n- Per-group statistics: resolved count, total estimate, total spent, resolved hours for each task type group\n\nPermission requirements:\n- Pool.canViewAll: Can view any user's pool\n- Pool.canViewOwnTeams: Can view pools of users in the same team (requires team membership verification)\n- Pool.canViewOwn: Can only view your own pool (accountableId must match authenticated user.id)\n\nTasks are prioritized horizontally within each group - tasks on the left have higher priority.\n\n**Note:** If you need to retrieve all tasks with custom filtering, sorting, or pagination capabilities, consider using the GET /tasks/grid endpoint instead. The grid endpoint provides more flexible filtering options and is better suited for retrieving large datasets or when you need to filter by multiple criteria simultaneously.","operationId":"TasksPoolController_getPool","parameters":[{"name":"accountableId","required":true,"in":"query","description":"UUID of the user whose pool data should be retrieved. **Important**: Tasks are filtered by the Accountable field (not the Assigned field). Only tasks where this user is set as the Accountable person will appear in the pool. The user must exist in the same workspace and not be deleted.","schema":{"example":"aff55c87-49c6-4d5d-9b0b-bf7478e2d504","type":"string"}}],"responses":{"200":{"description":"Successfully retrieved pool data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolResponseDto"}}}},"400":{"description":"Invalid accountableId UUID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks required Pool permissions or accountableId validation fails"},"404":{"description":"User (accountableId) not found, deleted, or not in same workspace"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get pool data for an accountable user","tags":["tasks","pool"],"x-required-scopes":["api:read"]}},"/tasks/pool/order":{"post":{"description":"Updates the horizontal priority order of tasks within a pool for a specific accountable user. Tasks are ordered from left to right, with tasks on the left having higher priority.\n\n**Important**: The pool contains tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included in the pool.\n\nThis endpoint updates the PoolOrder table, which stores the custom sort order for tasks within each accountable user's pool. The order is used when displaying tasks in the pool view, allowing users to prioritize which tasks should be handled first.\n\nThe newOrder array should contain all task IDs that need to be reordered, in the desired priority sequence (first item = highest priority, leftmost position).\n\nPermission requirements:\n- Pool.canEditAll: Can edit any user's pool order\n- Pool.canEditOwnTeams: Can edit pools of users in the same team (requires team membership verification)\n- Pool.canEditOwn: Can only edit your own pool order (accountableId must match authenticated user.id)\n\nThe accountableId must reference a valid user in the same workspace who is not deleted.","operationId":"TasksPoolController_updatePoolOrder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePoolOrderDto"}}}},"responses":{"200":{"description":"Successfully updated task order","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid accountableId UUID format or invalid newOrder array"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks required Pool edit permissions"},"404":{"description":"User (accountableId) not found, deleted, or not in same workspace"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task order within a pool","tags":["tasks","pool"],"x-required-scopes":["api:write"]}},"/tasks/pool/project-order":{"post":{"description":"Updates the vertical order of projects within a pool for a specific accountable user. Projects are displayed as rows in the pool view, and this endpoint allows you to customize which projects appear first.\n\n**Important**: The pool contains tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included in the pool.\n\nThis endpoint updates the PoolProjectOrder table, which stores the custom sort order for projects within each accountable user's pool. The order determines how projects are displayed vertically in the pool view.\n\nThe newOrder array should contain all project IDs that need to be reordered, in the desired sequence (first item = topmost position).\n\nPermission requirements:\n- Pool.canEditAll: Can edit any user's pool project order\n- Pool.canEditOwnTeams: Can edit pools of users in the same team (requires team membership verification)\n- Pool.canEditOwn: Can only edit your own pool project order (accountableId must match authenticated user.id)\n\nThe accountableId must reference a valid user in the same workspace who is not deleted.","operationId":"TasksPoolController_updatePoolProjectOrder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePoolProjectOrderDto"}}}},"responses":{"200":{"description":"Successfully updated project order","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid accountableId UUID format or invalid newOrder array"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks required Pool edit permissions"},"404":{"description":"User (accountableId) not found, deleted, or not in same workspace"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project order within a pool","tags":["tasks","pool"],"x-required-scopes":["api:write"]}},"/tasks/pool/group-order":{"post":{"description":"Updates the horizontal order of task type groups (e.g., Feature, Bug, Task) within a specific project in a pool for a specific accountable user. Groups are displayed as columns within each project row, and this endpoint allows you to customize which task types appear first.\n\n**Important**: The pool contains tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included in the pool.\n\nThis endpoint updates the PoolTypeProjectOrder table, which stores the custom sort order for task type groups within each project for each accountable user. The order determines how task types are displayed horizontally within each project row in the pool view.\n\nThe newOrder array should contain all task type IDs that need to be reordered, in the desired sequence (first item = leftmost position).\n\nPermission requirements:\n- Pool.canEditAll: Can edit any user's pool group order\n- Pool.canEditOwnTeams: Can edit pools of users in the same team (requires team membership verification)\n- Pool.canEditOwn: Can only edit your own pool group order (accountableId must match authenticated user.id)\n\nThe accountableId must reference a valid user in the same workspace who is not deleted. The projectId must reference a valid project in the same workspace.","operationId":"TasksPoolController_updatePoolGroupOrder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePoolGroupOrderDto"}}}},"responses":{"200":{"description":"Successfully updated group order","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid accountableId or projectId UUID format or invalid newOrder array"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks required Pool edit permissions"},"404":{"description":"User (accountableId) not found, deleted, or not in same workspace"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update group/type order within a project in a pool","tags":["tasks","pool"],"x-required-scopes":["api:write"]}},"/tasks/stack":{"get":{"description":"**What is the Stack?**\nThe Stack view shows a person's personal weekly schedule and serves as their personal task overview. It acts like a personal Kanban board that helps specialists work through tasks in a focused, distraction-free way. The Stack automatically follows the order from pipeline planning, showing exactly which tasks are planned for the employee and in what priority order.\n\n**Task Filtering Logic:**\nThe stack includes tasks from two sources:\n1. **Pipeline tasks**: Tasks that are in the user's pipeline for the current week (determined by the `today` parameter). These tasks appear regardless of whether they are assigned to the user or have the user as accountable. Pipeline tasks appear in the **toDo** and **done** columns.\n2. **Non-pipeline tasks**: Tasks filtered by the **Assigned field** (not the Accountable field). Only tasks where `assignedToId` equals the `userId` are included. These appear in **giveFeedback**, **getFeedback**, and **backlog** columns.\n\n**Important**: Unlike the Pool view which filters by Accountable field, the Stack filters non-pipeline tasks by the **Assigned field** (`assignedToId`). Pipeline tasks are included regardless of assignment or accountability.\n\n**What data is returned:**\nThe response contains tasks organized into five columns that show how far tasks are in the workflow:\n- **toDo**: Tasks that need to be worked on this week. These are tasks from the pipeline (regardless of assignment) that are in \"New\" or \"In Progress\" status.\n- **giveFeedback**: Tasks where the expert should give feedback to others. These are tasks where the user is **assigned** (filtered by Assigned field, not Accountable) and are in \"Feedback\" status.\n- **getFeedback**: Tasks that are waiting for feedback from others. These are tasks where the user is **NOT assigned** (filtered by Assigned field) but are in \"Feedback\" status.\n- **done**: Finished tasks from the pipeline (regardless of assignment) that are in \"Closed\" or \"Resolved\" status.\n- **backlog**: Tasks that are **assigned to the user** (filtered by Assigned field) but are not part of this week's pipeline. These could be planned for coming weeks.\n\n**Task sorting and prioritization:**\n- Tasks are sorted by priority (High > Normal > Low) then by shortNumber (ascending)\n- Pipeline tasks are prioritized and appear first in their respective columns\n- The order matches the priority set during pipeline planning\n\n**Permission requirements:**\nAccess is controlled by Stack permissions:\n- **Stack.canViewAll**: Can view any user's stack\n- **Stack.canViewOwnTeams**: Can view stacks of users in the same team\n- **Stack.canViewOwn**: Can only view own stack\n\n**Task details included:**\nEach task includes comprehensive information: ID, title, icon, short number, organization color and name, project details, status, summary, priority, type, deadline, estimated/spent time, comments count, assigned users, and tags. The spentTime field is only visible if the user has Tasks.seeLoggedTime permission.\n\n**Note:** If you need to retrieve all tasks with custom filtering, sorting, or pagination capabilities, consider using the GET /tasks/grid endpoint instead. The grid endpoint provides more flexible filtering options and is better suited for retrieving large datasets or when you need to filter by multiple criteria simultaneously.","operationId":"TasksStackController_getStack","parameters":[{"name":"userId","required":true,"in":"query","description":"UUID of the user whose stack to retrieve. **Important**: The stack includes (1) pipeline tasks regardless of assignment, and (2) non-pipeline tasks filtered by the Assigned field (where assignedToId equals this userId). The user must exist, not be deleted, and belong to the same workspace as the authenticated user.","schema":{"example":"aff55c87-49c6-4d5d-9b0b-bf7478e2d504","type":"string"}},{"name":"today","required":true,"in":"query","description":"Date in ISO 8601 format (YYYY-MM-DD) used for pipeline calculations. This date determines which tasks are included in the current week pipeline and affects task sorting and column assignment.","schema":{"example":"2025-11-04","type":"string"}}],"responses":{"200":{"description":"Stack data successfully retrieved. Returns tasks organized into five columns (toDo, giveFeedback, getFeedback, done, backlog). Pipeline tasks are included regardless of assignment. Non-pipeline tasks are filtered by the Assigned field (assignedToId). Includes comprehensive task details: IDs, titles, priorities, assignments, time tracking, and more.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StackResponseDto"}}}},"400":{"description":"Invalid request parameters. Occurs when userId is not a valid UUID format or today is not a valid ISO 8601 date format (YYYY-MM-DD).","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Validation failed: userId must be a UUID"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Access denied. Occurs when the authenticated user does not have the required Stack permissions (Stack.canViewAll, Stack.canViewOwnTeams, or Stack.canViewOwn) to view the requested user's stack, or when permission validation fails (e.g., user with Stack.canViewOwnTeams trying to view stack of user from different team, or user with Stack.canViewOwn trying to view another user's stack).","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":403},"message":{"type":"string","example":"Forbidden: You do not have permission to view this user's stack"}}}}}},"404":{"description":"User not found. Occurs when the userId does not exist, has been deleted (deletedAt is not null), or does not belong to the same workspace as the authenticated user.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":404},"message":{"type":"string","example":"User not found"}}}}}}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get stack data for a user","tags":["tasks"],"x-required-scopes":["api:read"]}},"/tasks/pipeline":{"get":{"description":"**What is the Pipeline?**\nThe Pipeline is Leadtime's weekly planning tool that provides a visual overview of all tasks scheduled for team members during a selected period. It shows team members vertically and calendar weeks horizontally, with each colored card representing a planned task. The pipeline is used in weekly planning meetings to schedule tasks according to importance and capacity, optimizing the use of available working time.\n\n**What data is returned:**\nThe response contains a complete pipeline view for the specified date range:\n- **users**: Object keyed by userId, each containing:\n  - Employee information (employeeId, userId, name)\n  - **days**: Array of day data including work day length, weekends, holidays, vacations, and sickness\n  - **items**: Array of pipeline items (planned tasks) with task details, dates, and times\n  - **reality**: Array of actual time logs (if enabled and user has permission)\n- **ready**: Boolean indicating if the week is marked as active/approved\n- **year** and **week**: ISO week number for the pipeline period\n\n**How it works:**\n- Pipeline items are filtered to include tasks that start, end, or span across the specified date range\n- The order of tasks in the pipeline automatically transfers to each team member's personal stack (Kanban board)\n- Dates are normalized to UTC and should be provided in ISO format (YYYY-MM-DD)\n- The pipeline supports two view modes: Overview (8-hour blocks) and Spacious (15-minute increments)\n\n**Permission-based Filtering:**\n- `usersFilter: \"all\"` - Requires Pipeline.canViewAll permission to view all users\n- `usersFilter: \"me\"` - Requires Pipeline.canViewOwnTeams or Pipeline.canViewAll permission to view own data\n- `usersFilter: \"team:{teamId}\"` - Requires Pipeline.canViewOwnTeams permission and user must be a member of the specified team\n\n**Reality Data (Time Logs):**\n- Set `withReality: true` to include actual booked working hours alongside planned tasks\n- Only includes time logs if user has Tasks.seeLoggedTime permission\n- If permission is missing, the reality array will be empty\n- This helps compare planned vs. actual work and improve future capacity estimates\n\n**Important - Understanding Time Format:**\nIn the response, `items[].dateStart` and `items[].dateEnd` contain both date and time components:\n- The **date part** (YYYY-MM-DD) represents the calendar day\n- The **time part** (HH:mm) represents hours since the beginning of the work day, NOT since midnight\n- Times are stored in 15-minute increments (0, 15, 30, 45, 60, etc.) from work day start\n- **Example**: If work day starts at 09:00:\n  - `\"2025-10-20T00:30:00Z\"` means 09:30 on Oct 20\n  - `\"2025-10-20T05:30:00Z\"` means 14:30 on Oct 20\n- When reading these values, extract the time component relative to work day start, not midnight\n\n**Use cases:**\n- Display weekly planning view in custom dashboards or integrations\n- Export pipeline data for reporting and analysis\n- Compare planned vs. actual work (with reality data)\n- Build custom planning tools that integrate with Leadtime's pipeline system","operationId":"PipelineController_getPipeline","parameters":[{"name":"dateStart","required":true,"in":"query","description":"Start date of the pipeline period in ISO format (YYYY-MM-DD). Pipeline items that start, end, or span across this date range will be included.","schema":{"example":"2025-10-20","type":"string"}},{"name":"dateEnd","required":true,"in":"query","description":"End date of the pipeline period in ISO format (YYYY-MM-DD). Pipeline items that start, end, or span across this date range will be included.","schema":{"example":"2025-11-02","type":"string"}},{"name":"usersFilter","required":true,"in":"query","description":"Filter which users to include in the pipeline view. Options: \"all\" (requires Pipeline.canViewAll permission to view all users), \"me\" (requires Pipeline.canViewOwnTeams or Pipeline.canViewAll permission to view own data), or \"team:{teamId}\" (requires Pipeline.canViewOwnTeams permission and user must be a member of the specified team).","schema":{"example":"me","type":"string"}},{"name":"withReality","required":false,"in":"query","description":"Include reality data (actual time logs) alongside planned tasks. When enabled, shows what team members actually worked on during the period. Only includes time logs if user has Tasks.seeLoggedTime permission. If permission is missing, the reality array will be empty. Useful for comparing planned vs. actual work.","schema":{"default":false,"example":false,"type":"boolean"}}],"responses":{"200":{"description":"Pipeline data successfully retrieved. Returns an object with users keyed by userId, ready status, year, and week.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineResponseDto"}}}},"400":{"description":"Invalid date format or filter value"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing required permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get pipeline data for date range","tags":["tasks","pipeline"],"x-required-scopes":["api:read"]}},"/tasks/pipeline/status":{"post":{"description":"**What this does:**\nToggles the active status of a pipeline week. When a week is marked as active, the planned tasks are automatically transferred to each team member's personal stack (Kanban board) in the correct order. This is typically done after the weekly pipeline planning meeting when all tasks have been agreed upon.\n\n**How it works:**\n- Creates a PipelineMeta record if it doesn't exist for the week (sets ready=true)\n- If PipelineMeta already exists, toggles the ready boolean value\n- The week is determined from the start date using ISO week numbering\n- Dates should be in ISO format (YYYY-MM-DD)\n\n**Status meanings:**\n- 🟢 **Active (ready=true)**: Current approved planning, tasks are visible in employee stacks\n- 🟠 **Inactive (ready=false)**: Draft for the upcoming week, still being planned\n\n**Required Permission**: Pipeline.canChangeSettings\n\n**Use cases:**\n- Mark a week as active after completing weekly planning meetings\n- Deactivate a week to make changes to the planning\n- Automate the activation process as part of a planning workflow","operationId":"PipelineController_activatePipeline","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivatePipelineDto"}}}},"responses":{"200":{"description":"Pipeline status successfully toggled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid date format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing required permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Activate/deactivate pipeline for a week","tags":["tasks","pipeline"],"x-required-scopes":["api:write"]}},"/tasks/pipeline/items":{"post":{"description":"**What this does:**\nCreates a new pipeline item, scheduling a task on the timeline for a specific team member. This is equivalent to adding a task to the pipeline during weekly planning meetings. The task will appear as a colored card in the pipeline view and, once the week is activated, will automatically appear in the assigned team member's personal stack in the correct order.\n\n**How it works:**\n- The pipeline item links a task to a user with specific start/end dates and times\n- The length of the pipeline card matches the planned working time\n- Tasks are placed in 15-minute increments for precise scheduling\n- The order of tasks in the pipeline determines their order in the team member's stack\n\n**Required Permission**: User must have either Pipeline.canPlanAll OR Pipeline.canPlanOwnTeams permission\n\n**Permission-based Assignment**:\n- Users with **Pipeline.canPlanAll** can assign tasks to any user in the workspace\n- Users with **Pipeline.canPlanOwnTeams** can only assign tasks to themselves or users in their teams\n\n**Validation Rules**:\n- **Date range**: Maximum 2 weeks between dateStart and dateEnd\n- **Time validation**: Start time must not exceed the work day length for the assigned user\n- **Task access**: Users can only plan tasks they have access to view\n- **Time increments**: Times must be in 15-minute increments (0, 15, 30, 45, 60, 75, 90, etc.)\n\n**Date/Time Format**: \n- **dateStart/dateEnd**: ISO format (YYYY-MM-DDTHH:mm:ssZ). **CRITICAL**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Always use \"T00:00:00Z\" or similar. Example: \"2025-11-04T00:00:00Z\"\n- **timeStart/timeEnd**: MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time)\n  - **Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)\n  - **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.\n  - **Calculation**: Actual time = workDayStartTime + (timeStart/timeEnd minutes)\n- **Examples** (assuming 8-hour work day starting at 09:00):\n  - `timeStart=0` = 09:00 (start of work day)\n  - `timeStart=60` = 10:00 (1 hour after work day start)\n  - `timeStart=240` = 13:00 (4 hours = midday)\n  - `timeStart=480` = 17:00 (8 hours = end of work day)\n- **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):\n  To schedule multiple tasks sequentially throughout the day, chain them together:\n  - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)\n  - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)\n  - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)\n  - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)\n  - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)\n- **Important**: timeStart/timeEnd are NOT hours since midnight. If work day starts at 09:00, timeStart=0 means 09:00, not 00:00.\n\n**Use cases:**\n- Programmatically add tasks to the pipeline during planning\n- Integrate with external planning tools\n- Automate task scheduling based on capacity calculations\n- Bulk import planned tasks from other systems","operationId":"PipelineController_createPipelineItem","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePipelineItemDto"}}}},"responses":{"201":{"description":"Pipeline item successfully created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineItemResponseDto"}}}},"400":{"description":"Invalid input data or validation failed"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing required permissions or not allowed to assign to target user"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Plan a task (create pipeline item)","tags":["tasks","pipeline"],"x-required-scopes":["api:write"]}},"/tasks/pipeline/items/{id}":{"patch":{"description":"**What this does:**\nUpdates an existing pipeline item. This allows you to modify the scheduling of a task that has already been planned - for example, moving it to a different time slot, assigning it to a different team member, or changing the task itself. All fields are optional; only provided fields are updated.\n\n**How it works:**\n- Partial updates: Only send the fields you want to change\n- If a field is not provided, the existing value is preserved\n- The same validation rules apply as when creating a pipeline item\n- Changes are reflected immediately in the pipeline view\n\n**Required Permission**: User must have either Pipeline.canPlanAll OR Pipeline.canPlanOwnTeams permission\n\n**Permission-based Assignment**:\n- Users with **Pipeline.canPlanAll** can assign to any user\n- Users with **Pipeline.canPlanOwnTeams** can only assign to themselves or users in their teams\n\n**Validation Rules**:\n- **Date range**: Maximum 2 weeks between dateStart and dateEnd\n- **Time validation**: Start time must not exceed the work day length for the user\n- **Task access**: Users can only plan tasks they have access to view\n- **Time increments**: Times must be in 15-minute increments (0, 15, 30, 45, 60, 75, 90, etc.)\n\n**Date/Time Format**: \n- **dateStart/dateEnd**: ISO format (YYYY-MM-DDTHH:mm:ssZ). **CRITICAL**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Always use \"T00:00:00Z\" or similar. Example: \"2025-11-04T00:00:00Z\"\n- **timeStart/timeEnd**: MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time)\n  - **Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)\n  - **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.\n  - **Calculation**: Actual time = workDayStartTime + (timeStart/timeEnd minutes)\n- **Examples** (assuming 8-hour work day starting at 09:00):\n  - `timeStart=0` = 09:00 (start of work day)\n  - `timeStart=60` = 10:00 (1 hour after work day start)\n  - `timeStart=240` = 13:00 (4 hours = midday)\n  - `timeStart=480` = 17:00 (8 hours = end of work day)\n- **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):\n  To schedule multiple tasks sequentially throughout the day, chain them together:\n  - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)\n  - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)\n  - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)\n  - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)\n  - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)\n- **Important**: timeStart/timeEnd are NOT hours since midnight. If work day starts at 09:00, timeStart=0 means 09:00, not 00:00.\n- **Response**: The response dateStart/dateEnd values show calculated absolute times in UTC. To interpret them, extract the time component relative to work day start, not midnight.\n\n**Use cases:**\n- Adjust task timing during planning meetings\n- Reassign tasks to different team members\n- Extend or shorten task duration\n- Move tasks to different days or weeks","operationId":"PipelineController_updatePipelineItem","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePipelineItemDto"}}}},"responses":{"200":{"description":"Pipeline item successfully updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineItemResponseDto"}}}},"400":{"description":"Invalid input data or validation failed"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing required permissions or not allowed to assign to target user"},"404":{"description":"Pipeline item not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Edit a planned task","tags":["tasks","pipeline"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this does:**\nSoft deletes a pipeline item, removing it from the pipeline view. The task itself is not deleted, only the planning/scheduling entry. This is useful when a task needs to be removed from the weekly plan but may be rescheduled later.\n\n**How it works:**\n- Performs a soft delete (marks as deleted, doesn't permanently remove)\n- The task remains in the system and can be re-planned later\n- The item disappears from the pipeline view immediately\n- If the week is already active, the task will also be removed from the team member's stack\n\n**Required Permission**: User must have either Pipeline.canPlanAll OR Pipeline.canPlanOwnTeams permission\n\n**Permission-based Deletion**:\n- Users with **Pipeline.canPlanAll** can delete any pipeline item in the workspace\n- Users with **Pipeline.canPlanOwnTeams** can only delete items assigned to themselves or users in their teams\n\n**Use cases:**\n- Remove tasks that are no longer needed in the weekly plan\n- Clear planning when tasks are cancelled or postponed\n- Adjust capacity by removing lower-priority items","operationId":"PipelineController_deletePipelineItem","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Pipeline item successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing required permissions or not allowed to delete this item"},"404":{"description":"Pipeline item not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Remove a planned task","tags":["tasks","pipeline"],"x-required-scopes":["api:write"]}},"/tasks/types":{"get":{"description":"Returns a list of all task types configured in the workspace. Task types structure workflows, bundle fields and statuses, and define how tasks are created and handled. Each task type specifies what kind of ticket is created, what information must be collected, and how the processing workflow works. This endpoint is optimized for common list data retrieval and includes translations, associated statuses, custom fields, and required fields for each type.","operationId":"TasksController_listTaskTypes","parameters":[{"name":"includeDeleted","required":false,"in":"query","description":"Include deleted task types in the response (default: true)","schema":{"default":true,"type":"boolean"}}],"responses":{"200":{"description":"Task types retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskTypeDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all task types","tags":["tasks","common-lists"],"x-required-scopes":["api:read"]}},"/tasks/statuses":{"get":{"description":"Returns a list of all task statuses configured in the workspace. Task statuses show the progress of a task in the ticket system and represent different phases in the workflow (e.g., New, In Progress, Feedback, Resolved, Closed, Backlog). Each status has a type, icon, sort order, and multilingual translations. This endpoint is optimized for common list data retrieval.","operationId":"TasksController_listTaskStatuses","parameters":[{"name":"includeDeleted","required":false,"in":"query","description":"Include deleted task statuses in the response (default: true)","schema":{"default":true,"type":"boolean"}}],"responses":{"200":{"description":"Task statuses retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskStatusDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all task statuses","tags":["tasks","common-lists"],"x-required-scopes":["api:read"]}},"/tasks/custom-fields":{"get":{"description":"Returns a list of all custom fields configured for tasks in the workspace. Custom fields capture project-specific data and add structured, multilingual input fields to tasks beyond the standard fields. Available field types include Text, Text Area, Number, Date, Checkbox, and Select (dropdown). Each field includes translations, sort order, and select options for dropdown fields. This endpoint is optimized for common list data retrieval.","operationId":"TasksController_listTaskCustomFields","parameters":[],"responses":{"200":{"description":"Task custom fields retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskCustomFieldDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all task custom fields","tags":["tasks","common-lists"],"x-required-scopes":["api:read"]}},"/tasks":{"post":{"description":"Creates a new task (ticket) in the specified project. Tasks are the core work units in Leadtime and represent actionable items that need to be completed. Each task belongs to a project, has a type (e.g., Feature, Bug), a status (e.g., New, In Progress), and can include assignments, deadlines, time estimates, custom fields, tags, and subtasks. The description field accepts HTML or Markdown format and will be automatically converted to the internal editor format. Project access is validated before task creation. Requires Tasks.create permission.","operationId":"TasksController_createTask","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskDto"}}}},"responses":{"200":{"description":"Task created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetailsResponseDto"}}}},"400":{"description":"Validation errors or invalid data","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"title":["Title is required"],"projectId":["Invalid project"],"typeId":["Invalid type"],"statusId":["Invalid status"],"priority":["Priority is required"],"description":["Description is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new task","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/grid":{"get":{"description":"Returns a paginated, filterable, and sortable grid of tasks across all projects in the workspace. This endpoint supports server-side filtering, sorting, and pagination for efficient data retrieval. You can filter by title, status, type, project, parent task, guest access, and more. Quick search is available on the title field. The default sort is by last updated timestamp in descending order.\n\n**What are \"Open\" Tasks?**\nA task is considered \"open\" when its status type is NOT `Closed`. This includes tasks with status types: `New`, `InProgress`, `Feedback`, `Backlog`, and `Resolved`. Only tasks with status type `Closed` are considered closed. You can filter for open tasks by using the `statusId` field with comparison `is_open` in your filters parameter.\n\nRetrieves a paginated grid of tasks with filtering and sorting capabilities. Quick search available on: title, summary. Filterable fields: title, shortNumber, statusId, typeId, lastUpdated, parentTaskId, guestAccess, projectId, assignedToId, userId, summary, priority, tags, isFavorite, inPipeline, hasObject, hasForms, billingStatus, billedAmount. Sortable fields: title, shortNumber, statusId, typeId, lastUpdated, parentTaskId, projectId, assignedToId, userId, summary, priority, billingStatus, billedAmount.","operationId":"TasksController_getGrid","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: title, summary","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **title** (string): Task title\n- **shortNumber** (number): Task short number\n- **statusId** (task_status): Task status ID. Special comparisons available: `is_open` (filters for tasks with status type NOT Closed - includes New, InProgress, Feedback, Backlog, Resolved), `is_closed` (filters for tasks with status type Closed).\n- **typeId** (task_type): Task type ID. Special comparisons available: `is_feature` (filters for tasks with type Feature), `is_bug` (filters for tasks with type Bug).\n- **lastUpdated** (date): Last updated timestamp\n- **parentTaskId** (set): Parent task ID (if subtask)\n- **guestAccess** (boolean): Whether task has guest access\n- **projectId** (set): Project ID\n- **assignedToId** (set): Assigned user ID\n- **userId** (set): Creator/accountable user ID\n- **summary** (string): Task summary\n- **priority** (set): Task priority (Low, Normal, High)\n- **tags** (array): Task tags\n- **isFavorite** (boolean): Whether task is marked as favorite by the current user\n- **inPipeline** (boolean): Whether the task appears on any team member pipeline in this workspace (non-deleted PipelineItem).\n- **hasObject** (boolean): Whether the task is linked to at least one non-deleted object instance in this workspace.\n- **hasForms** (boolean): Whether the task has at least one non-deleted form instance attached in this workspace.\n- **billingStatus** (set): Billing status (not_ready, waiting, charged, paid). Only available for external projects and users with Invoices.manage permission.\n- **billedAmount** (number): Total amount billed for this task. Only available for external projects and users with Invoices.manage permission.\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **title** (string): Task title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **shortNumber** (number): Task short number (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **statusId** (task_status): Task status ID. Special comparisons available: `is_open` (filters for tasks with status type NOT Closed - includes New, InProgress, Feedback, Backlog, Resolved), `is_closed` (filters for tasks with status type Closed). (task_status filter). Available comparisons: is_open, is_closed, is_empty, is_not_empty, in, not_in\n- **typeId** (task_type): Task type ID. Special comparisons available: `is_feature` (filters for tasks with type Feature), `is_bug` (filters for tasks with type Bug). (task_type filter). Available comparisons: is_feature, is_bug, is_empty, is_not_empty, in, not_in\n- **lastUpdated** (date): Last updated timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **parentTaskId** (set): Parent task ID (if subtask) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **guestAccess** (boolean): Whether task has guest access (boolean filter). Available comparisons: equal, not_equal\n- **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **assignedToId** (set): Assigned user ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **userId** (set): Creator/accountable user ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **summary** (string): Task summary (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **priority** (set): Task priority (Low, Normal, High) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **tags** (array): Task tags (array filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **isFavorite** (boolean): Whether task is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal\n- **inPipeline** (boolean): Whether the task appears on any team member pipeline in this workspace (non-deleted PipelineItem). (boolean filter). Available comparisons: equal, not_equal\n- **hasObject** (boolean): Whether the task is linked to at least one non-deleted object instance in this workspace. (boolean filter). Available comparisons: equal, not_equal\n- **hasForms** (boolean): Whether the task has at least one non-deleted form instance attached in this workspace. (boolean filter). Available comparisons: equal, not_equal\n- **billingStatus** (set): Billing status (not_ready, waiting, charged, paid). Only available for external projects and users with Invoices.manage permission. (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **billedAmount** (number): Total amount billed for this task. Only available for external projects and users with Invoices.manage permission. (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **title**: Task title\n- **shortNumber**: Task short number\n- **statusId**: Task status ID. Special comparisons available: `is_open` (filters for tasks with status type NOT Closed - includes New, InProgress, Feedback, Backlog, Resolved), `is_closed` (filters for tasks with status type Closed).\n- **typeId**: Task type ID. Special comparisons available: `is_feature` (filters for tasks with type Feature), `is_bug` (filters for tasks with type Bug).\n- **lastUpdated**: Last updated timestamp\n- **parentTaskId**: Parent task ID (if subtask)\n- **projectId**: Project ID\n- **assignedToId**: Assigned user ID\n- **userId**: Creator/accountable user ID\n- **summary**: Task summary\n- **priority**: Task priority (Low, Normal, High)\n- **billingStatus**: Billing status (not_ready, waiting, charged, paid). Only available for external projects and users with Invoices.manage permission.\n- **billedAmount**: Total amount billed for this task. Only available for external projects and users with Invoices.manage permission.\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** lastUpdated (desc)","schema":{"example":"[{\"field\":\"lastUpdated\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, title, icon, shortNumber, statusId, typeId, lastUpdated, parentTaskId, parentTitle, guestAccess, projectId, assignedToId, userId, summary, priority, tags, subTasksIds, isFavorite, hasActiveAgentSession, inPipeline, hasObject, hasForms, orgColor, billingStatus, billedAmount, spentTime, estimatedTime, deadline","schema":{"example":"id,title","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved tasks grid"},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get tasks grid","tags":["tasks","common-lists"],"x-required-scopes":["api:read"]}},"/tasks/{identifier}":{"get":{"description":"Returns complete task details including all metadata, description, comments, history, time logs, participants, subtasks, and custom fields. The description and comment bodies are returned in HTML format (converted from the internal editor format). Accepts both UUID (id) and numeric shortNumber identifiers. Access is controlled by project permissions and guest access settings. Organization members can only access tasks with guestAccess enabled.\n\n**Embedded images and files in HTML:** Rich text may contain `<div data-type=\"appImage\">`, `<div data-type=\"appFile\">`, or `<div data-type=\"appVideo\">` nodes with a `fileId` attribute (UUID) and `filename`. Images are **not** inlined as base64 in this API response.\n\n**Downloading embedded binaries for display or AI vision:** Use HTTP GET on the Leadtime **web application** origin (not the Public API base path): `/api/files/public/{fileId}`. Optional resized images: `/api/files/public/{fileId}/{maxWidth}` (and height if supported). The same `/api/files/public/...` pattern appears in other API responses (e.g. workspace upload, logos). Integrations and agents should parse `fileId` from the HTML, then fetch those URLs when they need to read image or file content.","operationId":"TasksController_getTaskDetails","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}}],"responses":{"200":{"description":"Task details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetailsResponseDto"}}}},"400":{"description":"Invalid identifier format (not UUID and not numeric)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get task details by UUID or shortNumber","tags":["tasks","common-lists"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates an existing task with partial data. Only provided fields will be updated, allowing you to modify specific properties without affecting others. The identifier can be either a UUID or a numeric short number (with or without # prefix). When updating the description, HTML or Markdown format is accepted and will be converted to the internal editor format. Changing the task type automatically updates the status if needed. Changing the project cascades to subtasks and time logs. Description mentions trigger notifications to mentioned users. Requires Tasks.editAnyTitleAndDescription permission. Organization members can only update tasks with guestAccess enabled.","operationId":"TasksController_patchTask","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or short number (e.g., \"550e8400-e29b-41d4-a716-446655440000\" or \"123\" or \"#123\")","schema":{"example":"123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTaskDto"}}}},"responses":{"200":{"description":"Task updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetailsResponseDto"}}}},"400":{"description":"Invalid request data or validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Access denied: No project access or permission denied"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update a task","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a task by its UUID or numeric shortNumber identifier. The task is marked as deleted but remains in the database for historical purposes. Access is controlled by Tasks.delete permission and project access. Billed tasks cannot be deleted to maintain billing integrity. Parent/subtask relationships are handled automatically - removing a parent task does not delete its subtasks, but removes the parent-child relationship. Organization members can only delete tasks with guestAccess enabled.","operationId":"TasksController_deleteTask","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}}],"responses":{"200":{"description":"Task deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid identifier format (not UUID and not numeric) or task is billed"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Access denied: No Tasks.delete permission, no project access, or OrganizationMember accessing non-guest task"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete task by UUID or shortNumber","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/duplicate":{"post":{"description":"Creates a copy of an existing task in the same project. The duplicated task keeps the source task configuration such as type, status, description, priority, assignee, accountable person, tags, custom fields, guest access, and task products. The new task title gets a ` (Copy)` suffix. Accepts either a UUID or a numeric short number (with or without `#` prefix). Organization members can only duplicate tasks with guest access enabled.","operationId":"TasksController_duplicateTask","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or short number (e.g., \"550e8400-e29b-41d4-a716-446655440000\" or \"123\" or \"#123\")","schema":{"example":"123","type":"string"}}],"responses":{"201":{"description":"Task duplicated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetailsResponseDto"}}}},"400":{"description":"Invalid identifier format (not UUID and not numeric)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Access denied: Missing Tasks.create permission, no project access, or OrganizationMember accessing a non-guest task"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Duplicate a task","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/comments":{"post":{"description":"Creates a new comment on a task. Comments are used for ongoing communication between participants and can be marked as internal (visible only to team members) or public (visible to guest users). The comment body accepts HTML or Markdown format and will be converted to the internal editor format. You can optionally update the task assignee, change the status, or log time in the same request. Mentions in comments (using @username) trigger notifications to mentioned users. Access is controlled by Tasks.addComments permission, project permissions, and guest access settings. Organization members can only comment on tasks with guestAccess enabled.","operationId":"TasksController_addComment","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTaskCommentDto"}}}},"responses":{"201":{"description":"Comment created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentResponseDto"}}}},"400":{"description":"Invalid identifier format or validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Add comment to task","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/comments/{commentId}":{"patch":{"description":"Updates the body of an existing task comment. Only the comment author can edit their own comment - this ensures comment integrity and prevents unauthorized modifications. The task identifier can be either a UUID or a numeric shortNumber. The comment body can be provided in HTML or Markdown format and will be converted to the internal editor format. The updated comment retains its original creation timestamp, author, and guest access settings.","operationId":"TasksController_updateTaskComment","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Comment UUID","schema":{"example":"aa0e8400-e29b-41d4-a716-446655440005","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskCommentDto"}}}},"responses":{"200":{"description":"Comment updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentResponseDto"}}}},"400":{"description":"Invalid identifier format, invalid comment data, or task not editable"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Not comment author, no project access, or OrganizationMember accessing non-guest task"},"404":{"description":"Task or comment not found, or comment already deleted"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update task comment","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a task comment. The comment is marked as deleted but remains in the database for historical purposes. Comment authors can always delete their own comments. Users with Tasks.deleteAnyComment permission can delete any comment, allowing moderators to remove inappropriate content. The task identifier can be either a UUID or a numeric shortNumber. Access is controlled by project permissions and guest access settings.","operationId":"TasksController_deleteTaskComment","parameters":[{"name":"identifier","required":true,"in":"path","description":"Task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Comment UUID","schema":{"example":"aa0e8400-e29b-41d4-a716-446655440005","type":"string"}}],"responses":{"200":{"description":"Comment deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid identifier format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Not comment author and no Tasks.deleteAnyComment permission, no project access, or OrganizationMember accessing non-guest task"},"404":{"description":"Task or comment not found, or comment already deleted"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete task comment","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/subtasks":{"post":{"description":"Adds an existing task as a subtask to the specified parent task. Subtasks allow you to break down complex tasks into smaller, more manageable units with clear assignments and better tracking. Each subtask works like its own ticket with its own description, assignee, priority, and status. The parent task identifier can be either a UUID or a numeric shortNumber. The subtask must exist and be accessible to the user. Requires Tasks.editAnyTitleAndDescription permission.","operationId":"TasksController_addSubtask","parameters":[{"name":"identifier","required":true,"in":"path","description":"Parent task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSubtaskDto"}}}},"responses":{"200":{"description":"Subtask added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetailsResponseDto"}}}},"400":{"description":"Invalid identifier format or validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Access denied: No access to project or task not editable"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Add a subtask to a task","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/tasks/{identifier}/subtasks/{subtaskId}":{"delete":{"description":"Removes the parent-child relationship between a task and its subtask by setting the subtask parentTaskId to null. The subtask itself is not deleted - it becomes a standalone task again. This is useful when reorganizing task hierarchies or when a subtask needs to be promoted to a main task. The parent task identifier can be either a UUID or a numeric shortNumber. The subtaskId must be a valid UUID. Requires Tasks.editAnyTitleAndDescription permission.","operationId":"TasksController_removeSubtask","parameters":[{"name":"identifier","required":true,"in":"path","description":"Parent task UUID or numeric shortNumber","schema":{"example":"550e8400-e29b-41d4-a716-446655440000 or 123","type":"string"}},{"name":"subtaskId","required":true,"in":"path","description":"Subtask UUID to remove","schema":{"example":"550e8400-e29b-41d4-a716-446655440001","type":"string"}}],"responses":{"200":{"description":"Subtask removed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetailsResponseDto"}}}},"400":{"description":"Invalid identifier format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Access denied: No access to project or task not editable"},"404":{"description":"Task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Remove a subtask from a task","tags":["tasks","common-lists"],"x-required-scopes":["api:write"]}},"/projects/{id}/billing-settings":{"get":{"description":"**What are Project Billing Settings?**\nProject billing settings define how invoices are created and structured for a specific project. These settings control invoice formatting, billing rules, default recipients, and support contingent (retainer) configurations.\n\n**What data is returned:**\n- **Invoice structure settings**: Whether to combine support and subscription invoices or split them separately\n- **Billing rules**: Whether bug tasks are billable\n- **Default invoice recipient**: The contact person from the customer organization who receives invoices\n- **Support contingent (retainer) configuration**: Monthly hour packages at fixed prices for recurring support/maintenance services\n- **Billing version snapshots**: Project versions used as the basis for invoicing (Single projects only)\n\n**Support Contingents (Retainers):**\nSupport contingents allow you to allocate monthly hour packages at a fixed price. They are perfect for recurring support, maintenance, or service tasks and get suggested automatically in invoice review. Unused hours can optionally be carried over to subsequent months.\n\n**Billing Versions:**\nFor Single projects, you can select which project version snapshot will be used as the basis for billing. This includes components, manual items, and products as agreed in that version. You can also optionally select a separate subscription billing version for products with recurring payments.\n\n**Permission requirements:**\nRequires Projects.seeAnyProject permission to view billing settings.","operationId":"ProjectBillingSettingsController_getBillingSettings","parameters":[{"name":"id","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Project billing settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetProjectBillingSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found or user does not have access"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project billing settings","tags":["projects","project-settings","billing"],"x-required-scopes":["api:read"]},"patch":{"description":"**How to update billing settings:**\nThis endpoint allows partial updates to project billing settings. Only the fields you provide will be updated; all other fields remain unchanged. The endpoint automatically merges your partial update with the current settings.\n\n**Invoice structure options:**\n- **allowMixedBilling (true)**: Combined invoices - bill support efforts (by booked hours) and subscriptions on a single invoice\n- **allowMixedBilling (false)**: Split invoices - support invoices are generated separately from subscription invoices\n\n**Billing rules:**\n- **bugsAreBillable**: Set whether time spent on tasks with \"Bug\" activity can be billed to the customer\n\n**Default invoice recipient:**\n- **invoiceContactId**: Select a contact person from the customer organization (OrganizationMember ID). This person will be auto-filled when creating invoices, but can be changed during invoice creation. Set to null to remove the default recipient.\n\n**Billing version snapshots (Single projects only):**\n- **billingProjectSnapshotId**: Select which project version will be used as the basis for billing. Includes components, manual items, and products as agreed in that version.\n- **productsBillingProjectSnapshotId**: Optionally select a separate version for billing subscriptions independently of the project scope. If empty, the main billing version is used automatically.\n\n**Permission requirements:**\nRequires both Projects.edit AND Invoices.manage permissions to update billing settings.","operationId":"ProjectBillingSettingsController_updateBillingSettings","parameters":[{"name":"id","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectBillingSettingsDto"}}}},"responses":{"200":{"description":"Project billing settings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid input - validation errors or missing required conditional fields"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Projects.edit or Invoices.manage permission"},"404":{"description":"Project not found or user does not have access"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project billing settings","tags":["projects","project-settings","billing"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/component-details/{id}":{"get":{"description":"Returns full details of a component from a project component tree including all nested children and metadata. \n\n**What are Project Components?**\nProject components are reusable project templates that standardize recurring project types. They connect requirements management, cost calculation, and execution in one unified workflow. Each component contains items (Epics, Work Packages, Todo Lists, Test Suites) that define the project structure.\n\n**Project Components vs Library Components:**\n- **Project Components** (this endpoint): Belong to a specific project, contain live data (answers to questions, test case evaluations, todo completion status), and can be connected to tasks in the task management system.\n- **Library Components**: Workspace-wide reusable templates (projectId is null) that serve as blueprints. They can be imported into projects to create project component instances.\n\n**Response Format:**\nAll ProseMirror editor content (description, internalNote) is automatically converted to HTML format for easy consumption. The response includes the complete hierarchical structure with all nested items and their metadata.\n\n**Access Control:**\nOnly project components that belong to the specified project (component.projectId equals projectId) are accessible through this endpoint.","operationId":"ProjectComponentsController_getComponentDetails","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentDetailsResponseDto"}}}},"400":{"description":"Invalid projectId or component id format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions or no access to project"},"404":{"description":"Component not found or does not belong to the specified project"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project component details","tags":["projects","components","questions"],"x-required-scopes":["api:read"]}},"/projects/{projectId}/components/tree":{"get":{"description":"Returns the complete hierarchical tree structure of components and their items for a specific project. Only non-deleted components and items are included in the response.\n\n**Component Tree Structure:**\nThe project tree organizes work into a clear hierarchy:\n- **Components**: Top-level containers that group related work packages. Examples include \"Website Development\", \"CRM Onboarding\", or \"E-commerce Setup\". Each component represents a major deliverable or project phase.\n- **Items**: Building blocks within components. Each item can be one of four types:\n  - **Epic**: Major sections or phases that group related work packages around a common theme (e.g., \"Technical Implementation\", \"Design & Development\")\n  - **Work Package**: Self-contained, clearly defined tasks that deliver tangible results (e.g., \"Set up hosting\", \"Design homepage layout\"). Work packages can have timeframes, questions, todos, and test cases.\n  - **Todo List**: Checklists of sub-steps or checkpoints perfect for recurring processes, quality control, or project preparation (e.g., \"Kickoff Checklist\", \"Quality Control\")\n  - **Test Suite**: Formal acceptance tests for project results. Contains multiple test cases with steps and expected results (e.g., \"Contact Form Tests\", \"SEO Review\")\n\n**Nesting and Relationships:**\nItems can be nested to any depth, allowing for complex project structures. Each Work Package can contain Questions (to capture customer requirements), Todos (checklist items that can be checked off), and Test Cases (individual test steps within a Test Suite).\n\n**Use Cases:**\nThis endpoint is ideal for displaying the complete project structure in a tree view, understanding project organization, and navigating the component hierarchy.","operationId":"ProjectComponentsController_getProjectTree","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project component tree retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentLibraryTreeResponseDto"}}}},"400":{"description":"Invalid project ID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get component tree for a specific project","tags":["projects","components","questions"],"x-required-scopes":["api:read"]}},"/projects/{projectId}/components/item-details/{id}":{"get":{"description":"Returns complete details of a component item from a project component tree including all test cases, todos, questions, conditions, and nested children.\n\n**What are Component Items?**\nItems are the building blocks within components that structure the project work. Each item can be one of four types:\n- **Epic**: Major sections or phases that group related work packages around a common theme (e.g., \"Technical Implementation\", \"Design & Development\"). Epics help track project progress at a strategic level.\n- **Work Package**: Self-contained, clearly defined tasks that deliver tangible results (e.g., \"Set up hosting\", \"Design homepage\"). Work packages can have timeframes, questions, todos, and test cases attached.\n- **Todo List**: Checklists of sub-steps or checkpoints perfect for recurring processes, quality control, or project preparation (e.g., \"Kickoff Checklist\", \"Quality Control\"). Each todo item can be checked off individually.\n- **Test Suite**: Formal acceptance tests for project results (e.g., \"Contact Form Tests\", \"SEO Review\"). Contains multiple test cases with steps and expected results. The project is considered finished when all test cases pass.\n\n**Attached Resources:**\nEach item can have various resources attached:\n- **Questions**: Capture customer-specific requirements or project details in a structured way (e.g., \"Do you need e-commerce functionality?\"). Answers can automatically affect effort calculation and pricing.\n- **Todos**: Checklist items within a Work Package or Todo List. Each todo can be checked off individually, supports comments, and tracks completion status with timestamps.\n- **Test Cases**: Individual test steps within a Test Suite. Each test case has steps, expected results, and can be evaluated as Passed, Failed, or PassedWithReservations.\n- **Conditions**: Rules that control when items are visible or active based on answers, todo completion, or test case status.\n\n**Response Format:**\nAll ProseMirror editor content (description, internalNote, question descriptions, test case steps, etc.) is automatically converted to HTML format. The response includes the complete item structure with all nested children, making it easy to display the full item hierarchy.\n\n**Access Control:**\nOnly project component items that belong to components in the specified project (component.projectId equals projectId) are accessible through this endpoint.","operationId":"ProjectComponentsController_getProjectComponentItemDetails","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component item UUID","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentItemDetailsResponseDto"}}}},"400":{"description":"Invalid projectId or id UUID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to view component item details or access to project denied"},"404":{"description":"Component item not found, is deleted, or does not belong to the specified project"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project component item details","tags":["projects","components","questions"],"x-required-scopes":["api:read"]}},"/projects/{projectId}/components":{"post":{"description":"Creates a new project component (reusable project template) with name, description, icon, tags, and internal notes.\n\n**What are Project Components?**\nProject components are reusable project templates that standardize recurring project types. They serve as blueprints for planning, pricing, and executing similar projects consistently. Components connect requirements management, cost calculation, and execution in one unified workflow.\n\n**After Creating a Component:**\nOnce created, you can add items (Epics, Work Packages, Todo Lists, Test Suites) to define the project structure. You can also add questions to capture customer requirements, todos for checklists, and test cases for acceptance testing.\n\n**Component Placement:**\nThe component is automatically appended to the end of the project component list. You can reorder components later using the sort endpoint.\n\n**Content Format:**\nDescription and internal note fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage. When retrieved, this content is converted back to HTML for easy consumption.\n\n**Side Effects:**\nCreating a component automatically triggers project configuration change tracking, marking the project as having been modified.","operationId":"ProjectComponentsController_createComponent","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectComponentDto"}}}},"responses":{"201":{"description":"Component created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectComponentResponseDto"}}}},"400":{"description":"Invalid request: missing required fields or invalid UUID format for tags"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to create components or access to project denied"},"404":{"description":"Project not found or not accessible"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new component in a project","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/import-from-library":{"post":{"description":"Imports one or more reusable component templates from the workspace component library into the specified project.\n\n**Component Library vs Project Components:**\nThe component system operates in two contexts:\n- **Library Components**: Workspace-wide reusable templates (projectId is null). These are blueprints with no live data - questions are defined but not answered, todos are not checked, test cases are not evaluated. Library components serve as templates that can be reused across multiple projects.\n- **Project Components**: Live instances in projects (projectId is set). These contain actual project data - questions can be answered, todos can be checked off, test cases can be evaluated, and items can be connected to tasks in the task management system.\n\n**What Gets Imported:**\nWhen you import a library component, the complete component structure is duplicated into the project, including:\n- All items (Epics, Work Packages, Todo Lists, Test Suites) with their complete hierarchy and nesting\n- All questions with their options and configurations (answers are initialized as empty/unanswered)\n- All todos with their descriptions (completion status is initialized as not done)\n- All test cases with steps and expected results (evaluations are initialized as not tested)\n- All conditions with their logic (automatically updated to reference the new item/question/todo/test case IDs in the project)\n\n**Automatic Processing:**\nAfter import, the system automatically:\n- Recalculates all conditions to ensure they reference the correct items in the project\n- Recalculates timeframes based on work packages and their configurations\n- Adds the imported components to the end of the project component list\n- Marks the project configuration as changed\n\n**Use Case:**\nThis endpoint is perfect for standardizing project setup. Create reusable templates in the library once, then import them into new projects to get a consistent starting structure that can be customized per project.","operationId":"ProjectComponentsController_importFromLibrary","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportComponentsFromLibraryDto"}}}},"responses":{"201":{"description":"Components imported successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportComponentsFromLibraryResponseDto"}}}},"400":{"description":"Invalid request: empty componentIds array or invalid UUID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to import components or access to project denied"},"404":{"description":"Project not found or component not found in library"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Import components from library into project","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/todos":{"post":{"description":"Creates a new todo checklist item within a project component item. Todos are actionable checklist items that help track progress and ensure nothing is missed.\n\n**What are Todos?**\nTodos are checklist items used to track actionable tasks within component items. They are perfect for:\n- Recurring processes (tests, approvals, installation steps)\n- Quality control checkpoints\n- Project preparation steps\n- Final checks before go-live\n- Kickoff checklists\n- Review and approval workflows\n\n**Todo Features:**\nEach todo can be:\n- Checked off individually to track completion\n- Commented on to add notes or context\n- Tracked with completion status (isDone), who completed it (doneBy), and when (doneUpdatedAt)\n\n**Where Todos Can Be Attached:**\nTodos can be attached to Work Packages or Todo List items. When attached to a Todo List, they become sub-items in a checklist. When attached to a Work Package, they serve as actionable steps within that work package.\n\n**Content Format:**\nThe description field accepts HTML or Markdown format and is automatically converted to IDoc format for storage. When retrieved, it is converted back to HTML for easy consumption.\n\n**Automatic Processing:**\nAfter creating a todo, the system automatically:\n- Calculates sort order as max(sort) + 1 for todos in the same item, positioning it at the end\n- Recalculates all conditions that depend on todo completion status\n- Updates timeframes if conditions changed\n- Marks the project configuration as changed\n\n**Use Case:**\nCreate todos to break down work packages into actionable steps, create quality checklists, or define approval workflows. Todos can also be used in conditions to control when other items are visible or active.","operationId":"ProjectComponentsController_createTodo","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTodoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoOperationResponseDto"}}}},"400":{"description":"Invalid request: invalid UUID format, missing required fields, or invalid content"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Component item not found or does not belong to the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new todo (checklist item) for a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/items/{itemId}/todos/{todoId}":{"patch":{"description":"Updates title and/or description of an existing todo item within a project component item. Only provided fields are updated (partial update). Description accepts HTML or Markdown format and is converted to IDoc for storage. Only todos belonging to component items in the specified project are accessible.","operationId":"ProjectComponentsController_updateTodo","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"todoId","required":true,"in":"path","description":"Todo UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440011","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTodoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoOperationResponseDto"}}}},"400":{"description":"Invalid request: no fields provided for update or invalid UUID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components"},"404":{"description":"Todo not found or does not belong to the specified item in the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing todo","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/todos/{id}":{"delete":{"description":"Deletes a todo item from a project component item (soft delete - sets deletedAt timestamp). Todos are checklist items within component items (epics, features, tasks) that can be marked as complete and have comments. This endpoint validates that the todo exists and belongs to a component item in the specified project. The service automatically triggers condition cleanup (removes conditions targeting the deleted todo), recalculates conditions if needed, and marks the project configuration as changed.","operationId":"ProjectComponentsController_deleteTodo","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"id","required":true,"in":"path","description":"Todo UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440011","type":"string"}}],"responses":{"200":{"description":"Todo deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid projectId, itemId, or todo id format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or no access to project"},"404":{"description":"Todo not found, component item not found, or item does not belong to the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete (soft delete) a todo from a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/testcases/{id}":{"delete":{"description":"Deletes a test case from a project component item (soft delete - sets deletedAt timestamp). Test cases are used to define test scenarios with steps and expected results for component items. This endpoint validates that the test case exists and belongs to a component item in the specified project. The service automatically triggers condition cleanup, recalculation, and timeframe updates.","operationId":"ProjectComponentsController_deleteTestCase","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"id","required":true,"in":"path","description":"Test case UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440010","type":"string"}}],"responses":{"200":{"description":"Test case deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid projectId, itemId, or testCaseId format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to delete test cases or no access to project"},"404":{"description":"Project not found, component item not found, or test case not found in the specified item"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete (soft delete) a test case","tags":["projects","components","questions"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an existing test case for a component item within a project. Only provided fields will be updated. Editor content (description, steps, expectedResult) accepts HTML or Markdown input when provided.","operationId":"ProjectComponentsController_updateTestCase","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440010","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"id","required":true,"in":"path","description":"Test case UUID","schema":{"example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTestCaseDto"}}}},"responses":{"200":{"description":"Test case updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentTestCaseDto"}}}},"400":{"description":"Invalid request: validation errors or test case does not belong to project"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project, item, or test case not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing test case","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/questions":{"post":{"description":"Creates a new question for a component item within a project component. Questions capture customer-specific requirements or project details in a structured way, making work packages flexible and reusable.\n\n**What are Questions?**\nQuestions are form fields attached to component items (typically Work Packages) that help capture customer requirements or project details. They make work packages flexible and reusable by allowing later individualization in projects. When customers answer questions, the answers can automatically affect effort calculation and pricing.\n\n**Question Types:**\nEach question can have one of eight types, each suited for different data collection needs:\n- **ShortText**: Single-line text input for names, titles, keywords, or short answers\n- **Editor**: Formatted multi-line text for descriptions, detailed explanations, or free text answers\n- **Checkbox**: Yes/No selection or multiple checkbox options. Answers can impact effort calculation if options have extraHours configured\n- **Radio**: Single choice from multiple predefined options. Answers can impact effort calculation if options have extraHours configured\n- **Files**: Upload files such as logos, documents, graphics, or reference materials\n- **Datepicker**: Select a date for deadlines, delivery dates, or scheduling\n- **Multiplier**: Dynamic calculation based on quantity. For example, \"How many pages?\" with a duration per page. The system calculates: Answer (quantity) × Duration per element = Total additional effort\n- **Person**: Select one or more people from the project team or clients\n\n**Key Features:**\n- **Automatic Effort Calculation**: Answers to Checkbox, Radio, and Multiplier questions can automatically affect the project effort and pricing\n- **Conditional Visibility**: Questions can have conditions that control when they are shown (e.g., only show if another question was answered a certain way)\n- **Flexible Options**: Checkbox and Radio questions support multiple options, each with optional extraHours that affect effort calculation\n\n**Validation Rules:**\nFor Checkbox and Radio question types, at least one option must be provided in the options array. The description field accepts HTML or Markdown input and is automatically converted to IDoc format for storage.\n\n**Automatic Processing:**\nAfter creating a question, the system automatically:\n- Assigns a sort order (max existing sort + 1) to position the question in the list\n- Recalculates all conditions to ensure dependent items are shown/hidden correctly\n- Updates timeframes if the question affects effort calculation\n- Triggers summary generation if needed\n\n**Use Case:**\nUse questions to create dynamic, customer-specific project configurations. For example, ask \"Do you need e-commerce functionality?\" and automatically add extra work packages or adjust pricing based on the answer.","operationId":"ProjectComponentsController_createQuestion","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectQuestionDto"}}}},"responses":{"201":{"description":"Question created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"e57dc37b-7693-4d06-b49c-17084b773aff","description":"ID of the created question"}}}}}},"400":{"description":"Invalid request: options array must have at least 1 item for Checkbox/Radio types, or invalid UUID/description format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project not found, item not found, or item does not belong to a component in the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new question for a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/questions/{id}":{"patch":{"description":"Updates only the provided fields of an existing question for a component item within a project. Questions support multiple types (ShortText, Editor, Checkbox, Radio, Files, Datepicker, Multiplier, Person). For Checkbox/Radio types, options must be provided if type is being updated to one of these types. The description field accepts HTML or Markdown input and is stored in IDoc format. Options management: options with id are updated, options without id are created, options removed from array are deleted. Conditions management: conditions with id are updated, conditions without id are created, conditions removed from array are deleted. This endpoint automatically recalculates conditions, updates timeframes, and triggers summary generation.","operationId":"ProjectComponentsController_updateQuestion","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Question UUID","schema":{"example":"f47dc37b-7693-4d06-b49c-17084b773baa","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectQuestionDto"}}}},"responses":{"200":{"description":"Question updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"f47dc37b-7693-4d06-b49c-17084b773baa","description":"ID of the updated question"}}}}}},"400":{"description":"Invalid request: options array must have at least 1 item for Checkbox/Radio types when type is updated, or invalid UUID/description format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project not found, item not found, question not found, or question does not belong to an item in the project"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update an existing question","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/questions/sort":{"post":{"description":"Reorders questions within a component item by providing a new sort order array of question IDs. The sort order determines the display order of questions in the UI and affects condition evaluation order. All questions for the item must be included in the newSortOrder array.","operationId":"ProjectComponentsController_sortProjectQuestions","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortProjectQuestionsDto"}}}},"responses":{"200":{"description":"Questions reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid UUID format or failed to sort questions"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Item not found or does not belong to a component in the project, or question IDs not found in the item"},"422":{"description":"Incomplete sort order: not all questions for the item are included in newSortOrder"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder questions within a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/questions/{id}/answer":{"post":{"description":"Submits an answer to a question within a project component item. This endpoint allows users to provide answers to questions that capture customer requirements or project details.\n\n**Question Types and Answer Formats:**\nQuestions can have different types, and each type requires a specific answer format. Only provide the answer field(s) relevant to the question type:\n- **ShortText**: Provide shortTextAnswer (string)\n- **Editor**: Provide editorAnswer (HTML or Markdown string)\n- **Checkbox**: Provide selectedOptions (array of option IDs for multiple selections)\n- **Radio**: Provide selectedOption (single option ID string)\n- **Files**: Provide fileUrls (array of file URL strings)\n- **Datepicker**: Provide dateAnswer (ISO 8601 date string)\n- **Multiplier**: Provide multiplierAnswer (number)\n- **Person**: Provide selectedPersonId (array of person IDs)\n\n**Automatic Processing:**\nWhen you submit an answer, the system automatically:\n- Tracks who answered (answeredBy is set to the current user)\n- Records when the answer was provided (answeredAt timestamp)\n- Calculates the isAnswered flag based on whether a valid answer was provided\n- Updates option selections for Checkbox/Radio types (marks options as selected)\n- Recalculates project conditions to show/hide dependent items based on the answer\n- Updates effort calculations if the question affects pricing (for Checkbox/Radio/Multiplier types)\n- Updates timeframes if effort changed\n\n**Permissions:**\nThis endpoint uses the processComponentsQuestions permission, which is more permissive than manageComponents. This allows users to answer questions and participate in project configuration without requiring full component edit access.","operationId":"ProjectComponentsController_answerQuestion","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component Item UUID","schema":{"example":"d47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Question UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnswerProjectQuestionDto"}}}},"responses":{"200":{"description":"Question answered successfully"},"400":{"description":"Invalid request: invalid UUID format, invalid date format, or invalid option IDs"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions (processComponentsQuestions) or access to project denied"},"404":{"description":"Question not found or question does not belong to the component item in the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Answer a question in a project component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/questions/{questionId}":{"delete":{"description":"Soft deletes a question from a component item within a project. The question must exist and belong to the specified component item in the project. This operation automatically cleans up invalid condition targets, recalculates conditions, updates component item timeframes, and marks the project configuration as changed.","operationId":"ProjectComponentsController_deleteQuestion","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}},{"name":"questionId","required":true,"in":"path","description":"Question UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"Question deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid project ID, item ID, or question ID format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Question not found, item not found, or item does not belong to the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a question from a project component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/items/{itemId}/testcases":{"post":{"description":"Creates a new test case for a component item within a project. Test cases document formal review and approval criteria for project results.\n\n**What are Test Cases?**\nTest cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results, ensuring that all delivered work meets the agreed requirements. Test cases are part of the acceptance testing process.\n\n**Test Case Structure:**\nEach test case consists of four main parts:\n- **Title**: Short, clear title that describes what is being tested (e.g., \"Check confirmation email destination\", \"Validate form input fields\")\n- **Description**: Explains what is being tested and why it matters. Provides context for the test.\n- **Steps**: Step-by-step instructions for running the test. Should be clear and reproducible (e.g., \"1. Navigate to login page 2. Enter credentials 3. Click login button\")\n- **Expected Result**: Describes the expected or correct system behavior when the test is run successfully (e.g., \"User is successfully logged in and redirected to dashboard\")\n\n**Test Case Evaluation:**\nAfter a test case is created, it can be evaluated later using the evaluate endpoint. Test cases can be marked as:\n- **Passed**: Test completed successfully and met all expected results\n- **Failed**: Test did not meet the expected result or revealed issues\n- **PassedWithReservations**: Test passed but with notes or concerns that should be documented\n\n**Project Completion:**\nThe project is considered finished when all test cases within all Test Suites have been passed successfully. This ensures quality and formal acceptance before project completion.\n\n**Content Format:**\nAll editor content fields (description, steps, expectedResult) accept HTML or Markdown input and are automatically converted to IDoc format for storage. When retrieved, they are converted back to HTML for easy consumption.\n\n**Use Case:**\nCreate test cases to define formal acceptance criteria for project deliverables. This ensures consistent quality standards and provides clear documentation of what was tested and approved.","operationId":"ProjectComponentsController_createTestCase","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440010","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTestCaseDto"}}}},"responses":{"201":{"description":"Test case created successfully. Returns the ID of the created test case.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440010"}}}}}},"400":{"description":"Invalid request: validation errors or item does not belong to project"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project or item not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new test case (acceptance test step) for a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/testcases/{id}/evaluate":{"post":{"description":"Mark a test case as passed or failed with optional comment. Uses Projects.processComponentsTestCases permission, allowing test evaluation without full component management access. Automatically sets testedBy and testedAt fields. Updates project configuration flag and recalculates conditions.","operationId":"ProjectComponentsController_evaluateTestCase","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item ID","schema":{"example":"550e8400-e29b-41d4-a716-446655440001","type":"string"}},{"name":"id","required":true,"in":"path","description":"Test case ID","schema":{"example":"550e8400-e29b-41d4-a716-446655440002","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluateTestCaseDto"}}}},"responses":{"200":{"description":"Test case evaluation saved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentTestCaseDto"}}}},"400":{"description":"Invalid request data or test case does not belong to component in project"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User does not have permission to evaluate test cases"},"404":{"description":"Test case not found or does not belong to the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Evaluate a test case","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/tree/sort":{"post":{"description":"Allows reordering components and items at the same level, reparenting items to different components or parent items, and moving items between components (automatically updates componentId for all descendants). Automatically handles side-effects including conditions recalculation, configurationChanged flag updates, summary generation checks, and timeframe updates.","operationId":"ProjectComponentsController_saveComponentsSort","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveComponentsSortDto"}}}},"responses":{"200":{"description":"Tree structure updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Products sort updated successfully"}}}}}},"400":{"description":"Invalid request format, invalid UUIDs, or invalid type values"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project not found or component/item not found in project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Move/reorder items in project component tree","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/tasks/generate":{"post":{"description":"Generate tasks from a component item and its children. The endpoint recursively processes items (two levels deep), skips Epic items, and only creates tasks for items that don't already have associated tasks. Can be used with either a Component ID or a ComponentItem ID.","operationId":"ProjectComponentsController_generateTasksFromItems","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"itemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateTasksFromItemsDto"}}}},"responses":{"200":{"description":"Tasks generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateTasksFromItemsResponseDto"}}}},"400":{"description":"Invalid request: invalid projectId, itemId format, or itemType value. Items may already be generating."},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project not found, item not found, or item does not belong to the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Generate tasks from a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/tasks/disconnect":{"post":{"description":"Disconnect a component item from its associated task(s). Optionally, the associated task(s) can be deleted after disconnection by setting deleteTask to true. All tasks connected to the item via entityUniqueId will be disconnected. If no tasks are connected to the item, a 404 error will be returned.","operationId":"ProjectComponentsController_disconnectItemFromTask","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"itemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisconnectItemFromTaskDto"}}}},"responses":{"200":{"description":"Item successfully disconnected from task(s)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid request: invalid projectId or itemId format, or validation error in request body"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project not found, item not found, item does not belong to the project, or no tasks are connected to the item"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Disconnect a component item from its associated task(s)","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/{id}":{"patch":{"description":"Updates only the provided fields of an existing component in a project. The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. Updating a component triggers project configuration change tracking.","operationId":"ProjectComponentsController_patchComponent","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectComponentDto"}}}},"responses":{"200":{"description":"Component updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectComponentResponseDto"}}}},"400":{"description":"Invalid request: invalid UUID format for component ID, project ID, or tag IDs"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Component not found, component does not belong to project, or project not accessible"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a project component","tags":["projects","components","questions"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a component by setting the deletedAt timestamp. The component is marked as deleted but not permanently removed from the database. Automatically cleans up invalid condition targets, recalculates project conditions, updates project configurationChanged flag, and updates timeframes.","operationId":"ProjectComponentsController_deleteComponent","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"Component deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid component ID or project ID format (not valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Component not found, component does not belong to project, component already deleted, or project not accessible"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete (soft delete) a component in a project","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/todos/{id}/toggle":{"post":{"description":"Toggles the completion status of a todo item within a project component item. Updates the isDone status, sets doneBy to the current user, and updates doneUpdatedAt timestamp. This endpoint uses the processComponentsTodos permission, allowing users to toggle todo completion without requiring full component management permissions. After toggling, the system recalculates conditions and marks the project configuration as changed.","operationId":"ProjectComponentsController_toggleTodo","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID (the item that contains the todo)","schema":{"example":"b58dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Todo UUID","schema":{"example":"c69dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleTodoDto"}}}},"responses":{"200":{"description":"Todo completion status toggled successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid UUID format or missing isDone field"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions or no access to project"},"404":{"description":"Todo not found or does not belong to a component item in the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Toggle todo completion status","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/todos/sort":{"post":{"description":"Reorders todos within a component item by providing a new sort order array. The sort order determines the display order of todos in the UI and affects condition evaluation order. All todos in the newSortOrder array must belong to the specified item. This operation automatically recalculates conditions if needed and marks the project configuration as changed.","operationId":"ProjectComponentsController_sortTodos","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortTodosDto"}}}},"responses":{"200":{"description":"Todos successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid projectId, itemId, or todo IDs in sort order"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions or no access to project"},"404":{"description":"Item not found or does not belong to the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Sort todos within an item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/testcases/sort":{"post":{"description":"Reorders test cases within a component item by providing a new sort order array. The sort order determines the display order of test cases in the UI and affects condition evaluation order. All test case IDs in the array must belong to the specified item.","operationId":"ProjectComponentsController_sortTestCases","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortTestCasesDto"}}}},"responses":{"200":{"description":"Test cases successfully sorted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid projectId, itemId, or test case IDs format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions or no access to project"},"404":{"description":"Project or item not found, or item does not belong to the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Sort test cases within a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/{id}/export":{"post":{"description":"Exports a project component to the workspace library, making it available for reuse across all projects. The exported component includes all nested data (items, questions, todos, test cases, conditions) with ID references automatically updated. The component is placed at the end of the library with an optional custom name. Requires both Projects.manageComponents and ProjectComponentsLibrary.edit permissions.","operationId":"ProjectComponentsController_exportComponentToLibrary","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component UUID to export","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportComponentToLibraryDto"}}}},"responses":{"201":{"description":"Component exported to library successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportComponentToLibraryResponseDto"}}}},"400":{"description":"Invalid projectId or component id format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or edit library, or no access to project"},"404":{"description":"Component not found, deleted, or does not belong to the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Export a project component to the library","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{id}":{"patch":{"description":"Updates only the provided fields of an existing component item (epic, feature, task) in a project. The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. When the conditions array is provided, it completely replaces existing conditions. Conditions with id are updated, conditions without id are created, and conditions not in the array are deleted. Updating an item triggers automatic side effects including condition recalculation, project configuration change tracking, and timeframe updates.","operationId":"ProjectComponentsController_patchItem","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component item UUID","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchComponentItemDto"}}}},"responses":{"200":{"description":"Item updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3047983b-d798-4aff-8dd4-628cf1900703"}}}}}},"400":{"description":"Invalid request: invalid UUID format for item ID, project ID, component ID, tag IDs, or invalid type/timeFrame combination"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Item not found, item does not belong to project component, component not found, or parent item not found"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a component item (epic, feature, task) in a project component. The item will be marked as deleted but not permanently removed. Automatically cleans up invalid condition targets and recalculates project conditions.","operationId":"ProjectComponentsController_deleteItem","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component item UUID","schema":{"example":"3047983b-d798-4aff-8dd4-628cf1900703","type":"string"}}],"responses":{"200":{"description":"Item deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid projectId or item id format"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Item not found or does not belong to a component in the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/tasks/connect":{"post":{"description":"Connects a component item (epic, feature, or task) to an existing task in the task management system. If the item already has a task connection, it will be disconnected first before establishing the new connection. Component tags are automatically merged into the task's tags. To disconnect an item from a task without connecting to a new one, pass null or omit the taskId in the request body.","operationId":"ProjectComponentsController_connectItemToTask","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component item UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectItemToTaskDto"}}}},"responses":{"200":{"description":"Item connected/disconnected successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid request: invalid UUID format for projectId, itemId, or taskId"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Project not found, item not found, item does not belong to project, or task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Connect component item to task","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items/{itemId}/todos/{id}/comment":{"post":{"description":"Adds or updates a comment on a todo item within a project component item. Todos are checklist items that can be marked as complete and have comments. The comment content is provided in HTML or Markdown format and converted to internal IDoc format. Uses the processComponentsTodos permission, allowing users to comment on todos without full component edit access. The system automatically recalculates project conditions and sets the project configurationChanged flag after adding a comment.","operationId":"ProjectComponentsController_addTodoComment","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Component Item UUID","schema":{"example":"d47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Todo UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTodoCommentDto"}}}},"responses":{"200":{"description":"Comment added successfully"},"400":{"description":"Invalid request: invalid UUID format or missing todoComment field"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions (processComponentsTodos) or access to project denied"},"404":{"description":"Todo not found or todo does not belong to the component item in the project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Add a comment to a todo in a project component item","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/{id}/duplicate":{"post":{"description":"Creates a copy of an existing component including all nested data (items, questions, todos, test cases, and conditions). The duplicated component is automatically named with \"(Copy)\" suffix and placed immediately after the original component in the sort order. Condition references are automatically updated to point to the new duplicated items. Triggers project configuration change tracking and recalculates conditions and timeframes.","operationId":"ProjectComponentsController_duplicateComponent","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component UUID to duplicate","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"201":{"description":"Component duplicated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateComponentResponseDto"}}}},"400":{"description":"Invalid component ID format (not a valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or no access to project"},"404":{"description":"Component not found or does not belong to the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Duplicate a component within the same project","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/components/{id}/recalculate-estimates":{"post":{"description":"Recalculates time estimates for all items (epics and non-epics) within a project component. This includes:\n- Recalculating individual item estimates based on timeFrame and question multipliers/extra hours\n- Recalculating epic estimates by summing their children's estimates\n- Updating the component's totalTimeFrame\n- Respecting visibility conditions (items with invalid conditions are excluded)\n\nItems are processed bottom-up (children first, then parents) to ensure accurate calculations. Only components that belong to the specified project can be recalculated through this endpoint.","operationId":"ProjectComponentsController_recalculateComponentEstimates","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"a47dc37b-7693-4d06-b49c-17084b773aff","type":"string"}},{"name":"id","required":true,"in":"path","description":"Component UUID","schema":{"example":"e57dc37b-7693-4d06-b49c-17084b773aff","type":"string"}}],"responses":{"200":{"description":"Estimates recalculated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid projectId or component ID format (not a valid UUID)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or no access to project"},"404":{"description":"Component not found or does not belong to the specified project"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Recalculate all estimates for a project component","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/items":{"post":{"description":"Creates a new component item within a project component. Items are the building blocks that structure project work.\n\n**What are Component Items?**\nItems are the building blocks within components that organize and structure project work. Each item can be one of four types:\n\n1. **Epic**: Major sections or phases that group related work packages around a common theme or goal\n   - Example: \"Technical Implementation\", \"Design & Development\", \"Project Kickoff & Preparation\"\n   - Can contain Work Packages, Todo Lists, or Test Suites\n   - Can nest other Epics to create complex hierarchies (arbitrary nesting depth)\n   - Helps track project progress at a strategic level\n   - TimeFrame is not required for Epics\n\n2. **Work Package**: Self-contained, clearly defined tasks that deliver tangible results\n   - Example: \"Set up domain and hosting\", \"Design homepage layout\", \"Configure CMS\"\n   - Can have timeframes/estimates (required field)\n   - Can contain Questions (to capture customer requirements), Todos (checklist items), and Test Cases (acceptance test steps)\n   - Can nest other Work Packages for complex tasks\n   - Acts as a draft before creating tickets in the task management system\n\n3. **Todo List**: Checklists of sub-steps or checkpoints\n   - Example: \"Kickoff Checklist\", \"Quality Control Checklist\", \"Go-live Approval\"\n   - Perfect for recurring processes (tests, approvals, installation steps)\n   - Each todo item within the list can be checked off individually\n   - TimeFrame is required (estimated time to complete the checklist)\n\n4. **Test Suite**: Formal acceptance tests for project results\n   - Example: \"Contact Form Tests\", \"Responsive Design Check\", \"SEO Review\"\n   - Contains multiple test cases with steps and expected results\n   - Project is considered finished when all test cases within all Test Suites pass\n   - TimeFrame is required (estimated time to run all tests)\n\n**Item Placement:**\nItems can be created at the root level of a component or nested under parent items (Epics or other Work Packages). This allows for flexible project structures that match your workflow.\n\n**Content Format:**\nThe description and internalNote fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage. When retrieved, they are converted back to HTML for easy consumption.\n\n**Validation Rules:**\nTimeFrame is required for all item types except Epic. The timeframe represents the estimated effort in hours for completing the item.","operationId":"ProjectComponentsController_createItem","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project UUID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComponentItemDto"}}}},"responses":{"201":{"description":"Component item created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComponentItemResponseDto"}}}},"400":{"description":"Invalid request: invalid UUID format, missing required fields, or validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions to manage components or access to project denied"},"404":{"description":"Component not found or does not belong to the specified project"},"422":{"description":"Editor content conversion errors"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new component item (Epic, Work Package, Todo List, or Test Suite)","tags":["projects","components","questions"],"x-required-scopes":["api:write"]}},"/projects/documentation":{"get":{"description":"Project documentation allows you to capture, structure, and share project-related knowledge directly in the project context. This helps keep important information like technical notes, repeated questions, and project-specific details organized and accessible.\n\nRetrieves a paginated grid of project documentation with filtering and sorting capabilities. Quick search available on: title. Filterable fields: id, projectId, projectName, title, userId, createdAt, updatedAt, sort. Sortable fields: id, projectId, projectName, title, userId, createdAt, updatedAt, sort.\n\n**Features:**\n- View all documentation articles across the workspace or filter by specific project\n- Each article includes title, author, and last update timestamp\n- Articles can be sorted and filtered using grid parameters\n\n**Use cases:**\n- Document project setup procedures and access credentials\n- Capture technical quirks and troubleshooting steps\n- Share project-specific notes and guidelines\n- Keep track of important decisions and context","operationId":"ProjectDocumentationController_listProjectDocumentation","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: title","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Documentation ID\n- **projectId** (set): Project ID\n- **projectName** (string): Project name\n- **title** (string): Documentation title\n- **userId** (string): Author user ID\n- **createdAt** (date): Creation timestamp\n- **updatedAt** (date): Last update timestamp\n- **sort** (number): Sort order\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Documentation ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **projectName** (string): Project name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **title** (string): Documentation title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **userId** (string): Author user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **sort** (number): Sort order (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Documentation ID\n- **projectId**: Project ID\n- **projectName**: Project name\n- **title**: Documentation title\n- **userId**: Author user ID\n- **createdAt**: Creation timestamp\n- **updatedAt**: Last update timestamp\n- **sort**: Sort order\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** sort (asc)","schema":{"example":"[{\"field\":\"sort\",\"direction\":\"asc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, projectId, projectName, title, userId, createdAt, updatedAt, sort","schema":{"example":"id,projectId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of project documentation articles"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project documentation articles","tags":["projects","project-documentation","projects"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project documentation article to capture and share project-related knowledge.\n\n**What to include:**\n- Clear, descriptive title that helps team members find the article quickly\n- Rich text content with formatting, headings, lists, and links\n- Project-specific information like access credentials, technical notes, or procedures\n\n**Content format:**\n- Accepts HTML or Markdown input\n- Content is automatically converted to internal ProseMirror format for storage\n- Supports rich text formatting including headings, lists, tables, and code blocks\n- When retrieved via GET endpoint, content is returned as HTML\n\n**Permissions:**\n- Requires \"createDocumentation\" permission for the project\n- Article is automatically associated with the authenticated user as the author","operationId":"ProjectDocumentationController_createProjectDocumentation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectDocumentationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDocumentationResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"projectId":["Project ID is required"],"title":["Title is required"],"content":["Content is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new project documentation article","tags":["projects","project-documentation","projects"],"x-required-scopes":["api:write"]}},"/projects/documentation/{id}":{"get":{"description":"Retrieves complete details of a specific project documentation article, including the full formatted content.\n\n**Returns:**\n- Article ID, project ID, and title\n- Full article content in HTML format (converted from internal ProseMirror format)\n- Author information (user ID who created the article)\n- Creation and last update timestamps\n\nThe content field contains HTML that can be directly rendered in a browser or HTML viewer. The content supports basic text formatting including headings, paragraphs, bold, italic, links, and lists.","operationId":"ProjectDocumentationController_getProjectDocumentationDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDocumentationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project documentation article details","tags":["projects","project-documentation","projects"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates specific fields of an existing project documentation article. Only provided fields will be updated; all other fields remain unchanged.\n\n**Update behavior:**\n- All fields are optional - only include fields you want to update\n- If content is provided, it replaces the entire article content\n- If projectId is provided, the article is moved to the specified project\n- If title is provided, only the title is updated\n\n**Content format:**\n- Accepts HTML or Markdown input (same as create endpoint)\n- Content is converted to internal format and stored\n- Supports rich text formatting including headings, lists, tables, and code blocks\n\n**Permissions:**\n- Requires \"manageDocumentation\" permission for the project\n- Original author information is preserved","operationId":"ProjectDocumentationController_patchProjectDocumentation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectDocumentationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDocumentationResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a project documentation article","tags":["projects","project-documentation","projects"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a project documentation article. The article is marked as deleted and will no longer appear in list queries, but it is not permanently removed from the database.\n\n**Deletion behavior:**\n- Article is marked with a deletedAt timestamp\n- Article will not appear in GET list queries\n- Article details can still be retrieved directly by ID if needed\n- Data is preserved for potential recovery or audit purposes\n\n**Permissions:**\n- Requires \"manageDocumentation\" permission for the project","operationId":"ProjectDocumentationController_deleteProjectDocumentation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a project documentation article","tags":["projects","project-documentation","projects"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/documents/contact-persons":{"get":{"description":"**What are contact persons?**\nContact persons are users who appear as the addressed recipient in project documents. They represent the client or stakeholder who will receive and review the document.\n\n**How contact persons are determined:**\n- For external projects (projects linked to an organization): Returns all members of the linked organization\n- For internal projects: Returns all employees in the workspace\n\n**What is returned:**\nA list of users with their ID, full name, position/title, and avatar. This list is used when creating or updating documents to select who should be listed as the contact person in the document header and recipient fields.\n\n**Use cases:**\n- Populate dropdown/select fields when creating documents\n- Display available contacts when editing document properties\n- Ensure only valid contacts are assigned to documents","operationId":"ProjectDocumentsController_getContactPersons","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Contact persons retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectDocumentUserResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get available contact persons for document creation","tags":["projects","documents"],"x-required-scopes":["api:read"]}},"/projects/{projectId}/documents":{"get":{"description":"**What are project documents?**\nProject documents are structured documents (estimates, specifications, contracts) that are automatically generated from project data. They are based on saved project versions (snapshots) and can include dynamic content, variables, and conditional sections.\n\n**Document types:**\n- **Estimate**: Commercial proposals summarizing project services, quantities, discounts, and costs\n- **Specification**: Detailed requirement documents describing what must be delivered\n- **Custom Editor**: Free-form documents like contracts, NDAs, or custom agreements\n\n**What is returned:**\nA paginated grid/list of all documents for the specified project, including:\n- Document title, type, and status\n- Associated project version (snapshot)\n- Contact person assigned to the document\n- Creation date\n- All fields can be filtered, sorted, and paginated\n\n**Document statuses:**\n- Draft: Initial version, not yet finalized\n- Waiting for Approval: Sent to client for review\n- Final: Ready for signing\n- Accepted: Approved by client\n- Rejected: Declined by client\n- Parked: Temporarily set aside\n\n**Important notes:**\n- Documents are always linked to a specific project version for traceability\n- Only documents from the current workspace are returned\n- Requires permission to view projects in the workspace\n\nRetrieves a paginated grid of documents with filtering and sorting capabilities. Quick search available on: title. Filterable fields: id, title, type, status, contactUserId, snapshotId, createdAt. Sortable fields: id, title, type, status, contactUserId, snapshotId, createdAt.","operationId":"ProjectDocumentsController_listDocuments","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: title","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Document ID\n- **title** (string): Document title\n- **type** (set): Document type (Estimate, Specification, CustomEditor)\n- **status** (set): Document status (Draft, Final, WaitingForApproval, etc.)\n- **contactUserId** (string): Contact person user ID\n- **snapshotId** (string): Project snapshot ID\n- **createdAt** (date): Document creation timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Document ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **title** (string): Document title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Document type (Estimate, Specification, CustomEditor) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **status** (set): Document status (Draft, Final, WaitingForApproval, etc.) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **contactUserId** (string): Contact person user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **snapshotId** (string): Project snapshot ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Document creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Document ID\n- **title**: Document title\n- **type**: Document type (Estimate, Specification, CustomEditor)\n- **status**: Document status (Draft, Final, WaitingForApproval, etc.)\n- **contactUserId**: Contact person user ID\n- **snapshotId**: Project snapshot ID\n- **createdAt**: Document creation timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, title, type, status, contactUserId, snapshotId, createdAt","schema":{"example":"id,title","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of project documents"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List documents for a project","tags":["projects","documents"],"x-required-scopes":["api:read"]},"post":{"description":"**What are project documents?**\nProject documents are structured documents automatically generated from project data. They can be estimates, specifications, or custom documents (contracts, NDAs, etc.) that pull in project, organization, and company data through variables.\n\n**Document creation workflow:**\n1. **Select project version**: Documents must be linked to a saved project version (snapshot) for traceability. Use GET /projects/:id/versions to get available versions.\n2. **Choose document type**: Estimate (commercial proposal), Specification (requirements), or CustomEditor (free-form document)\n3. **Set basic properties**: Title, description, status (usually starts as Draft)\n4. **Assign contact person**: Select from available contacts (use GET /projects/:id/documents/contact-persons)\n5. **Configure layout**: Enable table of contents, title page, indication mode (marks as non-binding estimate)\n6. **Set heading style**: Controls automatic numbering of headings (important for legal documents)\n7. **Add custom variables**: If using a template with custom variables, provide values for each variable\n\n**Content format:**\n- Description and customEditorContent accept HTML or Markdown\n- Content is automatically converted to internal IDoc format for storage\n- When retrieved, content is converted back to HTML for display\n- Supports rich formatting: headings, lists, tables, links, etc.\n\n**Custom variables:**\nIf your document template includes custom variables, provide them in the customVariables array:\n- Each variable needs: name, type, and value (matching the type)\n- Types: Text, LongText, Number, Boolean, Date, Select, MultiSelect\n- For Select/MultiSelect: provide options array and select value(s)\n- Required variables must have values provided\n\n**Validation:**\n- Project version must exist and belong to the project\n- Contact person must be available for this project type\n- All required custom variables must have values\n- Document type and status must be valid enum values\n\n**What is returned:**\nThe created document with all fields, including converted HTML content and custom variable values.\n\n**Use cases:**\n- Create new estimate for client approval\n- Generate specification document from project requirements\n- Create custom contract or agreement document\n- Automate document generation from project data","operationId":"ProjectDocumentsController_createDocument","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectDocumentDto"}}}},"responses":{"201":{"description":"Document created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDocumentResponseDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Permission denied"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create new document","tags":["projects","documents"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/documents/{id}":{"get":{"description":"**What is returned:**\nComplete details for a specific project document, including all metadata, content, and configuration.\n\n**Content format:**\n- Description and custom editor content are returned as HTML (converted from internal IDoc format)\n- This allows direct display in web browsers or further HTML processing\n- Original formatting, headings, lists, and tables are preserved\n\n**Custom variables:**\nIf the document uses custom variables (defined in templates), all variable definitions and their current values are included:\n- Variable name, type, and description\n- Current value (formatted according to variable type)\n- Available options (for Select/MultiSelect types)\n- Required flag indicating if value must be provided\n\n**Document configuration:**\n- Layout options: table of contents, title page, indication mode\n- Heading style: controls how headings are numbered (normal, sequential, sequential from level 2, sequential with paragraph signs)\n- Type and status: document category and workflow state\n- Project version binding: which project snapshot this document is based on\n\n**Use cases:**\n- Display document in a viewer/editor interface\n- Show document metadata and properties\n- Edit document content and variables\n- Export document to DOCX format\n- Track document status and history","operationId":"ProjectDocumentsController_getDocument","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Document ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Document retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDocumentResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Document not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get document details","tags":["projects","documents"],"x-required-scopes":["api:read"]},"patch":{"description":"**How partial updates work:**\nOnly fields provided in the request body will be updated. All other fields remain unchanged. This allows flexible updates without needing to send the entire document.\n\n**Content updates:**\n- If description or customEditorContent is provided, it replaces the existing content\n- If set to null or empty string, the content is cleared\n- If omitted, existing content is preserved\n- Content accepts HTML or Markdown and is converted to internal format\n\n**Custom variables:**\n- If customVariables array is provided, it completely replaces all existing custom variables\n- If omitted, existing custom variables are preserved\n- To update individual variables, include all variables (updated and unchanged) in the array\n\n**Validation:**\n- Project version (if provided) must exist and belong to the project\n- Contact person (if provided) must be available for this project type\n- All provided custom variables must have valid values matching their types\n\n**Common update scenarios:**\n- Change document status (e.g., Draft → Waiting for Approval)\n- Update document title or description\n- Modify custom variable values\n- Change contact person assignment\n- Update layout options (table of contents, title page, etc.)\n- Switch to a different project version\n\n**What is returned:**\nThe updated document with all fields, including converted HTML content and custom variable values.","operationId":"ProjectDocumentsController_updateDocument","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Document ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectDocumentDto"}}}},"responses":{"200":{"description":"Document updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDocumentResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Document not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update document","tags":["projects","documents"],"x-required-scopes":["api:write"]},"delete":{"description":"**How deletion works:**\nThis endpoint performs a soft delete - the document is marked as deleted but not permanently removed from the database. This preserves data integrity and allows for audit trails.\n\n**What happens:**\n- Document is marked with deletedAt timestamp\n- Document no longer appears in list queries (GET /projects/:id/documents)\n- Document details cannot be retrieved (returns 404)\n- Historical data is preserved for compliance and auditing\n\n**Validation:**\n- Document must exist and belong to the specified project\n- Document must belong to the current workspace\n- Requires permission to manage documents\n\n**Use cases:**\n- Remove documents that are no longer needed\n- Clean up draft documents\n- Archive outdated versions\n- Maintain data retention policies\n\n**Note:** This is a permanent operation. There is no undo functionality. If you need to restore a deleted document, contact support.","operationId":"ProjectDocumentsController_deleteDocument","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Document ID","schema":{"type":"string"}}],"responses":{"204":{"description":"Document deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Document not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete document","tags":["projects","documents"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/documents/preview-offer-number/{snapshotId}":{"get":{"description":"Generates a preview formatted offer number that will be assigned when the document is created. This is used for UI display purposes only. Requires snapshotId to format with correct version.","operationId":"ProjectDocumentsController_getPreviewOfferNumber","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}},{"name":"snapshotId","required":true,"in":"path","description":"Snapshot ID for version information","schema":{"type":"string"}}],"responses":{"200":{"description":"Preview formatted offer number","content":{"application/json":{"schema":{"type":"object","properties":{"formattedOfferNumber":{"type":"string","example":"WKM-1-v2-27012026-1"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get preview formatted offer number for new document","tags":["projects","documents"],"x-required-scopes":["api:read"]}},"/projects/{projectId}/interim-payments":{"get":{"description":"Returns all interim payments (partial payments, advance payments, prepayments) for the specified project.\n\n**What are Interim Payments?**\nInterim payments are payments made before the final project invoice is completed. They help maintain cash flow during long-running projects, split large invoices into manageable parts, and track incoming payments according to agreed payment schedules.\n\n**Typical use cases:**\n- Contractually agreed down payment at project start\n- Installment after milestone approval\n- Security payment before development start\n\n**What is returned:**\n- All interim payments for the project, sorted by their sort order\n- Each payment includes: title, description (as HTML), amount, billing status, creation date, and sort order\n- The description field is automatically converted from internal IDoc format to HTML for API responses\n\n**Important:**\n- Only non-deleted payments are returned\n- Payments marked as billed (isBilled: true) are still included in the list\n- Requires Projects.seeAnyProject permission","operationId":"ProjectInterimPaymentsController_listInterimPayments","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Interim payments retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectInterimPaymentResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project interim payments","tags":["projects","project-settings","billing","interim-payments"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new interim payment (partial payment, advance payment, prepayment) in the specified project.\n\n**How it works:**\n- The new payment is added to the project with the provided title, description, and amount\n- Sort order is automatically set to the maximum existing sort order plus 1 (appears at the end of the list)\n- The payment is initially marked as not billed (isBilled: false)\n- Description can be provided as HTML or Markdown and is automatically converted to internal IDoc format\n\n**Required fields:**\n- **title**: Short label for the payment (e.g., \"Prepayment\", \"50% Deposit\", \"Milestone 1\")\n- **amount**: Payment amount (must be positive, minimum 0.01)\n\n**Optional fields:**\n- **description**: Detailed notes about the payment (e.g., \"Client paid €1,000 upfront\"). Can be provided as HTML or Markdown format\n\n**What happens after creation:**\n- The payment becomes available for billing in the invoicing system\n- When used in an invoice, it is automatically deducted from the final invoice amount\n- The payment can be reordered using the sort endpoint\n\n**Validation:**\n- Amount must be a positive number (minimum 0.01)\n- Title cannot be empty\n- Project must exist and be accessible\n\n**Permissions:** Requires Projects.edit permission","operationId":"ProjectInterimPaymentsController_createInterimPayment","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectInterimPaymentDto"}}}},"responses":{"201":{"description":"Interim payment created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create interim payment","tags":["projects","project-settings","billing","interim-payments"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/interim-payments/{id}":{"patch":{"description":"Updates only the provided fields of an existing interim payment. Fields not included in the request remain unchanged.\n\n**How partial updates work:**\n- Only fields included in the request body are updated\n- Omitted fields retain their current values\n- Description can be updated with new HTML or Markdown content\n- Sort order is not changed (use the sort endpoint to reorder payments)\n\n**Updatable fields:**\n- **title**: Update the payment label\n- **description**: Update payment notes (HTML or Markdown format)\n- **amount**: Change the payment amount (must be positive, minimum 0.01)\n\n**Important restrictions:**\n- Cannot update the isBilled status (this is managed automatically by the invoicing system)\n- Cannot change the project ID (payment is tied to the project)\n- Cannot update creation date or user ID\n\n**Validation:**\n- If amount is provided, it must be a positive number (minimum 0.01)\n- If title is provided, it cannot be empty\n- Payment must exist and belong to the specified project\n\n**Permissions:** Requires Projects.edit permission","operationId":"ProjectInterimPaymentsController_patchInterimPayment","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectInterimPaymentDto"}}}},"responses":{"200":{"description":"Interim payment updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Interim payment not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update interim payment","tags":["projects","project-settings","billing","interim-payments"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft-deletes an interim payment by marking it as deleted. The payment is not permanently removed from the database.\n\n**How deletion works:**\n- The payment is marked with a deletedAt timestamp\n- Deleted payments no longer appear in list queries\n- The payment is not permanently removed (soft delete)\n- Historical data and billing records remain intact\n\n**Important considerations:**\n- If the payment has already been billed (isBilled: true), deletion may affect invoice calculations\n- Consider the impact on invoicing before deleting billed payments\n- Deleted payments can be filtered out from API responses\n\n**Permissions:** Requires Projects.edit permission","operationId":"ProjectInterimPaymentsController_deleteInterimPayment","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Interim payment deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Interim payment not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete interim payment","tags":["projects","project-settings","billing","interim-payments"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/interim-payments/sort":{"post":{"description":"Updates the sort order of interim payments by providing a new ordered list of payment IDs.\n\n**How reordering works:**\n- Provide an array of payment IDs in the desired display order\n- The first ID in the array gets sort order 0, the second gets 1, and so on\n- Payments not included in the array retain their current sort order\n- This affects the order in which payments appear in list responses\n\n**Example:**\nTo display payments in order: Payment A, Payment C, Payment B\nSend: { \"paymentIds\": [\"id-of-A\", \"id-of-C\", \"id-of-B\"] }\n\n**Validation:**\n- All payment IDs must be valid UUIDs\n- All payment IDs must belong to the specified project\n- Array cannot be empty\n\n**Use cases:**\n- Organize payments chronologically (e.g., first payment, second payment, final payment)\n- Group related payments together\n- Prioritize certain payments in the display\n\n**Permissions:** Requires Projects.edit permission","operationId":"ProjectInterimPaymentsController_sortInterimPayments","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortInterimPaymentsDto"}}}},"responses":{"200":{"description":"Interim payments reordered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder interim payments","tags":["projects","project-settings","billing","interim-payments"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/manual-positions":{"get":{"description":"**What are Manual Positions?**\nManual positions are extra costs that do not come from the product catalog. They are used to bill for external costs, third-party services, flat fees, and other expenses that fall outside the automatic system logic. Typical examples include printing costs, translation services, license fees, travel expenses, or one-time flat rates.\n\nManual positions are part of the project planning process and will appear in project quotes and invoices. They can be categorized, discounted, and reordered as needed.\n\n**What this endpoint returns:**\n- All active manual positions for the specified project\n- Positions are sorted by their sort order (ascending)\n- Only current positions are returned (snapshot positions are excluded)\n- Descriptions are returned as HTML (converted from internal IDoc format)\n\n**Permission requirements:**\n- Requires read access to the project\n- Requires api:read scope\n\n**Use cases:**\n- Display manual positions in project planning interface\n- Show billable items that will appear in quotes and invoices\n- Review project cost breakdown before creating offers","operationId":"ProjectManualPositionsController_listManualPositions","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ManualPositionResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all manual positions for a project","tags":["projects"],"x-required-scopes":["api:read"]},"post":{"description":"**What this endpoint does:**\nCreates a new manual position (billable item) for a project. Manual positions represent extra costs that are not automatically tracked by the system, such as external services, third-party expenses, or one-time fees.\n\n**How it works:**\n- The position is added to the project's current planning\n- Sort order is automatically assigned (new positions are added at the end)\n- Description can be provided as HTML or Markdown (automatically converted to internal format)\n- The position can optionally be assigned to a category for better organization\n- Once created, the position will appear in project quotes and invoices\n\n**Description format:**\n- Accepts HTML or Markdown format\n- The system automatically detects the format and converts it to internal IDoc format\n- Empty descriptions are allowed (will be stored as empty IDoc)\n- In responses, descriptions are always returned as HTML\n\n**Validation rules:**\n- Name is required and must not be empty\n- Price must be a number greater than or equal to 0\n- Category ID must be a valid UUID if provided\n- Project must exist and user must have access\n\n**Permission requirements:**\n- Requires Projects.manageManualPositions permission\n- Requires api:write scope\n- Requires read access to the project\n\n**Use cases:**\n- Add external costs like printing or translation services\n- Record one-time fees that are not in the product catalog\n- Include retroactive charges or corrections to existing offers\n- Add services that happened outside the operational project structure","operationId":"ProjectManualPositionsController_createManualPosition","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateManualPositionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualPositionResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new manual position","tags":["projects"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/manual-positions/{id}":{"patch":{"description":"**What this endpoint does:**\nUpdates an existing manual position. Supports partial updates - you only need to provide the fields you want to change.\n\n**How partial updates work:**\n- Only provided fields are updated\n- Fields not included in the request remain unchanged\n- To remove a category, set categoryId to null\n- Description can be updated with new HTML or Markdown content\n\n**Description updates:**\n- If description is provided, it will be converted from HTML/Markdown to internal IDoc format\n- If description is not provided, the existing description remains unchanged\n- Empty descriptions are allowed\n\n**Validation rules:**\n- Position must exist and belong to the specified project\n- Name must not be empty if provided\n- Price must be greater than or equal to 0 if provided\n- Category ID must be a valid UUID if provided (or null to remove)\n- Project must exist and user must have access\n\n**Permission requirements:**\n- Requires Projects.manageManualPositions permission\n- Requires api:write scope\n- Requires read access to the project\n\n**Use cases:**\n- Correct pricing or descriptions\n- Update position details before creating quotes\n- Reassign positions to different categories\n- Modify positions based on client feedback","operationId":"ProjectManualPositionsController_updateManualPosition","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchManualPositionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualPositionResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing manual position","tags":["projects"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this endpoint does:**\nSoft deletes a manual position by setting the deletedAt timestamp. The position is not permanently removed from the database but is hidden from normal queries and will not appear in project quotes or invoices.\n\n**How soft deletion works:**\n- The position is marked as deleted with a timestamp\n- It remains in the database for historical reference\n- Deleted positions are excluded from list queries\n- The position can be referenced in historical project snapshots\n\n**Validation rules:**\n- Position must exist and belong to the specified project\n- Project must exist and user must have access\n\n**Permission requirements:**\n- Requires Projects.manageManualPositions permission\n- Requires api:write scope\n- Requires read access to the project\n\n**Use cases:**\n- Remove positions that are no longer needed\n- Clean up project planning before finalizing quotes\n- Remove incorrectly added positions","operationId":"ProjectManualPositionsController_deleteManualPosition","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a manual position","tags":["projects"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/manual-positions/sort":{"post":{"description":"**What this endpoint does:**\nUpdates the sort order of manual positions within a project. The order determines how positions appear in project planning views, quotes, and invoices.\n\n**How reordering works:**\n- Provide an array of position IDs in the desired display order\n- The first ID in the array will have sort order 0, the second will have sort order 1, and so on\n- All positions must belong to the specified project\n- Positions not included in the array will maintain their current sort order (but may appear after the reordered positions)\n\n**Validation rules:**\n- All position IDs must be valid UUIDs\n- All positions must belong to the specified project\n- Project must exist and user must have access\n- Duplicate IDs are not allowed\n\n**Permission requirements:**\n- Requires Projects.manageManualPositions permission\n- Requires api:write scope\n- Requires read access to the project\n\n**Use cases:**\n- Organize positions by importance or category\n- Group related positions together\n- Control the order in which positions appear in quotes and invoices\n- Improve readability of project planning overview","operationId":"ProjectManualPositionsController_sortManualPositions","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortManualPositionsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder manual positions","tags":["projects"],"x-required-scopes":["api:write"]}},"/projects/journal":{"get":{"description":"Retrieves a paginated grid of project journal entries with filtering and sorting capabilities. Filterable fields: id, projectId, createdAt, createdBy, mood, reminder. Sortable fields: id, projectId, createdAt, createdBy, mood, reminder.\n\n**What are Project Journals?**\nProject journals are chronological records of important project events, observations, feedback, risks, and internal notes. They act as a timeline-based memory for projects, helping stakeholders track developments, assess communication, and prevent knowledge loss.\n\n**What is returned:**\n- Journal entry identification (ID, projectId)\n- Content (body as HTML, mood indicator)\n- Metadata (createdAt, createdBy, lastUpdated)\n- Optional reminder date for follow-ups\n\n**Journal Entry Features:**\n- **Mood indicators**: Sad (negative reactions, problems), Neutral (factual observations), Happy (positive feedback, wins)\n- **Rich text content**: Supports formatted text with paragraphs, highlighting, and structuring\n- **Reminder function**: Optional date-based reminders for follow-ups or review points\n\n**Filtering and search:**\n- Filter by projectId to view entries for a specific project\n- Filter by mood, creation date, creator, or reminder date\n- Sort by any sortable field (default: createdAt descending)\n- Server-side pagination for large journal lists\n\n**Permissions:**\nAutomatically filters journal entries based on user permissions. Users without Projects.seeAnyProject permission will only see journal entries for projects they have access to.","operationId":"ProjectsJournalController_listProjectJournals","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Journal entry ID\n- **projectId** (string): Project ID\n- **createdAt** (date): Journal entry creation timestamp\n- **createdBy** (string): Creator user ID\n- **mood** (set): Journal entry mood\n- **reminder** (date): Reminder date\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Journal entry ID\n- **projectId**: Project ID\n- **createdAt**: Journal entry creation timestamp\n- **createdBy**: Creator user ID\n- **mood**: Journal entry mood\n- **reminder**: Reminder date\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, projectId, createdAt, createdBy, mood, reminder, body","schema":{"example":"id,projectId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of project journal entries"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project journal entries","tags":["projects","journal","projects"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project journal entry to document important project events, observations, feedback, or notes.\n\n**What to include:**\n- **projectId** (required): The UUID of the project this entry belongs to\n- **body** (required): The journal entry content in HTML or Markdown format. Supports rich text formatting including paragraphs, headings, highlighting, and lists\n- **mood** (optional): Mood indicator - Sad (problems/escalations), Neutral (factual), or Happy (wins/progress). Defaults to Neutral if not provided\n- **reminder** (optional): ISO 8601 date string for setting a reminder about this entry\n\n**Common use cases:**\n- Documenting customer feedback or complaints\n- Recording internal findings and observations\n- Capturing important conversations or decisions\n- Noting risks or opportunities\n- Tracking project mood and sentiment over time\n\n**Validation:**\n- The projectId must exist and be accessible to the authenticated user\n- Body content cannot be empty\n- Reminder date must be a valid ISO 8601 date string\n\n**What is returned:**\nThe complete created journal entry with all fields populated, including the body converted to HTML format.","operationId":"ProjectsJournalController_createProjectJournal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectJournalResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"projectId":["Project ID is required"],"body":["Body content is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project journal entry","tags":["projects","journal","projects"],"x-required-scopes":["api:write"]}},"/projects/journal/{id}":{"get":{"description":"Returns complete details for a specific project journal entry, including the full formatted content.\n\n**What is returned:**\n- Journal entry identification (ID, projectId)\n- Complete body content converted to HTML format with all formatting preserved\n- Mood indicator (Sad, Neutral, or Happy)\n- Creation metadata (createdAt, createdBy)\n- Last update timestamp (lastUpdated)\n- Optional reminder date (null if not set)\n\n**Use cases:**\n- Displaying a full journal entry in detail view\n- Editing an existing entry (use this to get current values before updating)\n- Retrieving formatted content for display in external systems\n\n**Note:** Returns 404 if the journal entry does not exist, has been deleted, or is not a project journal entry.","operationId":"ProjectsJournalController_getProjectJournalDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectJournalResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project journal entry details","tags":["projects","journal","projects"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates one or more fields of an existing project journal entry. Only provided fields will be updated; all other fields remain unchanged.\n\n**What can be updated:**\n- **projectId**: Move the entry to a different project (must be accessible to user)\n- **body**: Update the journal entry content (HTML or Markdown format)\n- **mood**: Change the mood indicator (Sad, Neutral, or Happy)\n- **reminder**: Update or set a reminder date (ISO 8601 format). Set to null to clear an existing reminder\n\n**Update behavior:**\n- All fields are optional - only include fields you want to change\n- If a field is not provided, its current value is preserved\n- For reminder: provide a date string to set/update, or null to clear\n- The lastUpdated timestamp is automatically updated when any field changes\n\n**Validation:**\n- If projectId is provided, it must exist and be accessible to the authenticated user\n- Body content cannot be empty if provided\n- Reminder date must be a valid ISO 8601 date string if provided\n\n**What is returned:**\nThe complete updated journal entry with all fields, including the body converted to HTML format.","operationId":"ProjectsJournalController_patchProjectJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectJournalResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update project journal entry","tags":["projects","journal","projects"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a project journal entry. The entry is marked as deleted and will no longer appear in list queries, but it is not permanently removed from the database.\n\n**Deletion behavior:**\n- The entry is marked with a deletedAt timestamp\n- Deleted entries do not appear in the list endpoint results\n- The entry data remains in the database for audit purposes\n- This operation cannot be undone through the API\n\n**Note:** Returns 404 if the journal entry does not exist, has already been deleted, or is not a project journal entry.","operationId":"ProjectsJournalController_deleteProjectJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project journal entry","tags":["projects","journal","projects"],"x-required-scopes":["api:write"]}},"/projects/uploads":{"get":{"description":"Retrieves a paginated grid of project uploads with filtering and sorting capabilities. Quick search available on: fileName, description. Filterable fields: id, projectId, fileId, fileName, description, categoryId, mimeType, fileSize, userId, updatedAt. Sortable fields: id, projectId, fileId, fileName, description, categoryId, mimeType, fileSize, userId, updatedAt.\n\n**What are Project Uploads?**\nProject uploads allow you to centrally manage project-related files within Leadtime. This feature provides organized storage for documents like contracts, offers, technical specifications, or internal notes directly within project context, eliminating the need to search through external folders.\n\n**What is returned:**\n- Upload identification (ID, project ID, file ID)\n- File information (filename, MIME type, file size in bytes)\n- Organization details (description, category ID)\n- User information (user ID who created or last updated the upload)\n- Timestamps (creation and last update dates)\n\n**Filtering and search:**\n- Quick search across file name and description fields\n- Advanced filtering by project ID, category, file type, user, and more\n- Sorting by any sortable field (default: updatedAt descending)\n- Server-side pagination for large upload lists\n\n**Access control:**\nThis endpoint automatically filters uploads based on project access permissions:\n- Users with Projects.seeAnyProject permission see all uploads across all projects\n- Other users see only uploads from projects they have access to\n- Optional filtering by projectId via grid filters allows focusing on specific projects\n\n**Note:** This endpoint requires the Projects.manageUploads permission.","operationId":"ProjectUploadsController_listProjectUploads","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: fileName, description","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Upload ID\n- **projectId** (set): Project ID\n- **fileId** (string): File ID\n- **fileName** (string): File name\n- **description** (string): Upload description\n- **categoryId** (set): Category ID\n- **mimeType** (string): MIME type\n- **fileSize** (number): File size in bytes\n- **userId** (set): User ID who created the upload\n- **updatedAt** (date): Last update timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Upload ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **fileId** (string): File ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **fileName** (string): File name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **description** (string): Upload description (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **categoryId** (set): Category ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **mimeType** (string): MIME type (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **fileSize** (number): File size in bytes (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **userId** (set): User ID who created the upload (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Upload ID\n- **projectId**: Project ID\n- **fileId**: File ID\n- **fileName**: File name\n- **description**: Upload description\n- **categoryId**: Category ID\n- **mimeType**: MIME type\n- **fileSize**: File size in bytes\n- **userId**: User ID who created the upload\n- **updatedAt**: Last update timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** updatedAt (desc)","schema":{"example":"[{\"field\":\"updatedAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, projectId, fileId, fileName, description, categoryId, mimeType, fileSize, userId, updatedAt","schema":{"example":"id,projectId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of project uploads"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project uploads","tags":["projects","uploads","projects"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project upload record, linking an uploaded file to a project.\n\n**How to upload files:**\n1. First, upload the file using POST /workspace/upload to obtain a fileId\n2. Then use that fileId in this endpoint to create the project upload record\n\n**Required fields:**\n- projectId: The project to attach the upload to (must exist and user must have access)\n- fileId: The file ID from the workspace upload endpoint (must exist in workspace)\n\n**Optional fields:**\n- description: Free text field for classifying the content (e.g., \"Signed contract 10/28/2025\", \"Kickoff protocol\")\n- categoryId: Thematic classification for organizing uploads (e.g., Contract, Offer, Documentation)\n\n**Validation:**\n- Project must exist and belong to the workspace\n- User must have access to the project\n- File must exist and belong to the workspace\n- Category must exist and belong to the workspace (if provided)\n\n**What is returned:**\nComplete upload details including file metadata, timestamps, and user information.\n\n**Note:** This endpoint requires the Projects.manageUploads permission.","operationId":"ProjectUploadsController_createProjectUpload","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectUploadDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUploadResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"projectId":["Project ID is required"],"fileId":["File ID is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project upload","tags":["projects","uploads","projects"],"x-required-scopes":["api:write"]}},"/projects/uploads/{id}":{"get":{"description":"Retrieves complete details for a specific project upload, including all file metadata and organization information.\n\n**What is returned:**\n- Upload identification (ID, project ID, file ID)\n- File metadata (filename, MIME type, file size in bytes)\n- Organization details (description, category ID)\n- User information (user ID who created the upload)\n- Timestamps (creation and last update dates in ISO 8601 format)\n\n**Note:** This endpoint requires the Projects.manageUploads permission. Only uploads from projects the user has access to can be retrieved.","operationId":"ProjectUploadsController_getProjectUploadDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUploadResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project upload details","tags":["projects","uploads","projects"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates specific fields of an existing project upload. Only provided fields are updated; all fields are optional.\n\n**What can be updated:**\n- projectId: Change which project the upload belongs to (must exist and user must have access)\n- description: Update the description text (set to null to clear)\n- categoryId: Change or remove the category (set to null to remove category assignment)\n\n**Note:** The file itself cannot be changed. To replace a file, delete the upload and create a new one.\n\n**Validation:**\n- Upload must exist and belong to the workspace\n- If projectId is provided, project must exist and user must have access\n- If categoryId is provided, category must exist and belong to the workspace\n\n**What is returned:**\nComplete updated upload details including all current field values.\n\n**Note:** This endpoint requires the Projects.manageUploads permission.","operationId":"ProjectUploadsController_patchProjectUpload","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectUploadDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUploadResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update project upload","tags":["projects","uploads","projects"],"x-required-scopes":["api:write"]},"delete":{"description":"Removes a project upload from the project. This performs a soft delete, meaning the upload is marked as deleted but not permanently removed from the database. The file itself remains in the workspace file storage.\n\n**What happens:**\n- The upload record is marked as deleted (deletedAt timestamp is set)\n- The upload no longer appears in project upload lists\n- The underlying file remains in workspace storage\n\n**Validation:**\n- Upload must exist and belong to the workspace\n- User must have access to the project containing the upload\n\n**Note:** This endpoint requires the Projects.manageUploads permission.","operationId":"ProjectUploadsController_deleteProjectUpload","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project upload","tags":["projects","uploads","projects"],"x-required-scopes":["api:write"]}},"/projects/{id}/document-settings":{"get":{"description":"Returns document settings for a specific project including current values and inherited defaults from organization/workspace hierarchy.\n\n**What are Project Document Settings?**\nProject document settings control formatting options and contact information for automatically generated documents such as proposals, contracts, requirement specifications, and other project documents. These settings can override organization/workspace defaults on a per-project basis, allowing you to customize document appearance and structure for specific projects.\n\n**Settings Overview:**\n- **Title Page**: Controls whether generated documents include a title page\n- **Table of Contents**: Determines if an automatically generated table of contents is embedded (especially helpful for long documents)\n- **Heading Style**: Controls the numbering and formatting of headings (Normal, Sequential, SequentialFromSecondLevel, SequentialWithParagraphs)\n- **Default Contact Person**: Sets the contact person automatically when creating new documents (useful for recurring offer processes and automated preambles)\n\n**Inheritance System:**\nSettings support a three-level inheritance hierarchy:\n1. **Project-level settings**: Project-specific overrides (null = inherit from organization)\n2. **Organization-level defaults**: Organization-specific defaults (null = inherit from workspace)\n3. **Workspace-level defaults**: Global workspace defaults\n\nThe response includes both current project values and the inherited defaults from the organization/workspace hierarchy. Null values in current settings indicate that the project inherits from organization/workspace defaults. The \"*Default\" fields show what values are inherited when project settings are null.","operationId":"ProjectDocumentSettingsController_getDocumentSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetProjectDocumentSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project document settings","tags":["projects","project-settings"],"x-required-scopes":["api:read"]},"patch":{"description":"Partially updates document settings for a specific project. All fields are optional - only provided fields will be updated.\n\n**What are Project Document Settings?**\nProject document settings control formatting options and contact information for automatically generated documents such as proposals, contracts, requirement specifications, and other project documents. These settings can override organization/workspace defaults on a per-project basis.\n\n**Settings You Can Update:**\n- **projectDocumentTitlePage**: Enable/disable title page for generated documents (null = use organization standard)\n- **projectDocumentEnableToc**: Enable/disable table of contents in generated documents (null = use organization standard)\n- **projectDocumentHeadingStyle**: Set heading numbering style (null = use organization standard)\n  - Normal: No numbering\n  - Sequential: Numbering from level 1\n  - SequentialFromSecondLevel: Numbering from level 2\n  - SequentialWithParagraphs: Numbering with symbol prefix (e.g., §)\n- **projectDocumentDefaultContactUserId**: Set default contact person UUID for new documents (null = use organization standard or no default)\n\n**Inheritance Behavior:**\nSetting any field to null enables inheritance from organization/workspace defaults. This allows you to override organization settings for specific projects or revert to using organization standards.\n\n**Use Cases:**\n- Customize document formatting for specific project types\n- Set project-specific contact persons for recurring offer processes\n- Override organization defaults when needed\n- Revert to organization standards by setting fields to null","operationId":"ProjectDocumentSettingsController_updateDocumentSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectDocumentSettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project document settings","tags":["projects","project-settings"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/subscription-billings":{"get":{"description":"**What are Project Subscription Billings?**\nProject Subscription Billings allow you to manage recurring fees and charges for projects. This feature is ideal for SaaS subscriptions, maintenance contracts, support packages, or any recurring billing arrangement. The system automatically generates billing rows based on your date range and frequency settings, which can then be included in invoices.\n\n**What data is returned:**\n- All active subscription billing items for the specified project\n- Items are sorted by their sort order (display order)\n- Each item includes date range, payment type, price, frequency, and configuration details\n\n**Payment types supported:**\n- **fixed**: Fixed monthly fee (e.g., $500/month support package)\n- **perUnit**: Variable pricing based on units (e.g., per user, per license)\n- **oneTime**: One-time charge within the subscription period\n\n**Use cases:**\n- Retrieve all recurring billing items for a project\n- Display subscription information in project dashboards\n- Generate reports on recurring revenue\n- Audit subscription configurations","operationId":"ProjectSubscriptionBillingsController_listSubscriptionBillings","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Subscription billings retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectSubscriptionBillingResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found or access denied"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List project subscription billings","tags":["projects","project-settings","billing","subscriptions"],"x-required-scopes":["api:read"]},"post":{"description":"**What this endpoint does:**\nCreates a new recurring subscription billing item for a project. The system automatically generates billing rows for each billing period based on your date range and frequency settings. These rows can later be included in invoices.\n\n**How it works:**\n1. Specify the date range (fromDate to toDate) for when the subscription is active\n2. Set the frequency (1-12 months) to determine billing intervals:\n   - 1 = monthly billing\n   - 3 = quarterly billing\n   - 6 = semi-annual billing\n   - 12 = yearly billing\n3. Choose the payment type:\n   - **fixed**: Fixed amount per period (e.g., $500/month)\n   - **perUnit**: Variable pricing (e.g., $50 per user per month)\n   - **oneTime**: One-time charge (requires quantity field)\n4. The system automatically:\n   - Calculates sort order (display position)\n   - Generates billing rows for each period within the date range\n   - Creates date keys for invoice matching\n\n**Validation rules:**\n- fromDate must be before or equal to toDate\n- Price must be positive\n- Frequency must be between 1 and 12 months\n- Quantity is required for oneTime payment type\n\n**Permissions required:**\n- Projects.edit: Ability to edit project settings\n- Invoices.manage: Ability to manage invoice configurations\n\n**Example use cases:**\n- Set up monthly support package subscription\n- Configure quarterly maintenance contract\n- Add yearly license renewal\n- Create variable pricing based on usage","operationId":"ProjectSubscriptionBillingsController_createSubscriptionBilling","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectSubscriptionBillingDto"}}}},"responses":{"201":{"description":"Subscription billing created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid date range, frequency, or other validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions (requires Projects.edit and Invoices.manage)"},"404":{"description":"Project not found or access denied"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project subscription billing","tags":["projects","project-settings","billing","subscriptions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/subscription-billings/{id}":{"patch":{"description":"**What this endpoint does:**\nPartially updates an existing subscription billing item. You can update any combination of fields without providing all values. The system automatically regenerates billing rows if the date range or frequency changes.\n\n**How updates work:**\n- Only provide the fields you want to change (all fields are optional)\n- If you update fromDate, toDate, or frequency, the system will:\n  - Delete unbilled billing rows\n  - Regenerate billing rows based on new date range and frequency\n  - Preserve already billed rows (they are not affected)\n- Other fields (price, title, payment type) update immediately without regenerating rows\n\n**Important notes:**\n- If updating both fromDate and toDate, ensure fromDate is before or equal to toDate\n- Changing frequency will affect all future billing periods\n- Already billed rows remain unchanged\n- Sort order is preserved unless explicitly changed\n\n**Permissions required:**\n- Projects.edit: Ability to edit project settings\n- Invoices.manage: Ability to manage invoice configurations\n\n**Example use cases:**\n- Extend subscription end date\n- Change billing frequency (e.g., from monthly to quarterly)\n- Update subscription price\n- Modify subscription title or description\n- Change payment type","operationId":"ProjectSubscriptionBillingsController_updateSubscriptionBilling","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectSubscriptionBillingDto"}}}},"responses":{"200":{"description":"Subscription billing updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid date range, frequency, or other validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions (requires Projects.edit and Invoices.manage)"},"404":{"description":"Project or subscription billing not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update project subscription billing","tags":["projects","project-settings","billing","subscriptions"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this endpoint does:**\nSoft deletes a subscription billing item. The item is marked as deleted and will no longer appear in listings or be included in future invoice generation. However, the data is retained in the database for historical records and audit purposes.\n\n**What happens when deleted:**\n- The subscription billing item is marked with a deletedAt timestamp\n- It disappears from all active listings and queries\n- Already billed rows remain unchanged (historical invoices are not affected)\n- Unbilled billing rows are effectively cancelled\n- The item can be restored if needed (though not through this API)\n\n**Permissions required:**\n- Projects.edit: Ability to edit project settings\n- Invoices.manage: Ability to manage invoice configurations\n\n**Use cases:**\n- Cancel an active subscription\n- Remove a subscription that is no longer needed\n- Clean up test or incorrect subscription entries","operationId":"ProjectSubscriptionBillingsController_deleteSubscriptionBilling","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Subscription billing deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions (requires Projects.edit and Invoices.manage)"},"404":{"description":"Project or subscription billing not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project subscription billing","tags":["projects","project-settings","billing","subscriptions"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/subscription-billings/products-from-snapshot":{"get":{"description":"**What this endpoint does:**\nReturns products from the current billing snapshot for Single type projects. These products represent what will be used for invoice generation and show how invoice rows will be formed.\n\n**What data is returned:**\n- Products from the billing snapshot (productsBillingProjectSnapshotId or billingProjectSnapshotId)\n- Only for Single type projects\n- Products are sorted by their sort order\n- Includes product details: name, prices, quantities, active dates, etc.\n\n**Use cases:**\n- Display products that will be used for billing\n- Preview how invoice rows will be formed\n- Show read-only product information for subscription billing","operationId":"ProjectSubscriptionBillingsController_getProductsFromBillingSnapshot","parameters":[{"name":"projectId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Products retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found or access denied"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get products from billing snapshot","tags":["projects","project-settings","billing","subscriptions"],"x-required-scopes":["api:read"]}},"/projects/{projectId}/products":{"get":{"description":"Returns an array of product summary objects for a project. Products are sorted by sort field (ascending) and only non-deleted products are returned.\n\nProducts in Leadtime are standardized services or items from the product catalog that have been imported into a project. They can represent one-time purchases, subscriptions, or quantity-based services. Each product includes pricing information, quantity, active date ranges, and calculated final prices based on selected variants and options.\n\nThe response includes:\n- Product identification (id, name, finalName)\n- Category and logo information\n- Quantity and pricing (fixed, subscription, per-unit)\n- Final calculated prices (includes variant and option adjustments)\n- Active date range (activeFrom, activeTo, isActive flag)","operationId":"ProjectProductsController_listProducts","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Products retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectProductResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Project does not exist"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all products in a project","tags":["projects","products"],"x-required-scopes":["api:read"]},"post":{"description":"Imports a standardized product from the product catalog into a project. The product is copied into the project, allowing project-specific configuration without affecting the original catalog entry.\n\n**How it works:**\n1. Select a product from the catalog by providing its productId\n2. Set the quantity for this product in the project\n3. Optionally select an active variant (e.g., Standard, Pro, Enterprise)\n4. Select any required options and optional add-ons\n\n**Product Isolation:**\nWhen imported, the product becomes a copy within the project. Any subsequent changes to the product in this project do not affect the original catalog entry. This ensures historical accuracy for quotes and invoices.\n\n**Pricing:**\nThe final price is calculated based on:\n- Base product pricing (fixed, subscription, or per-unit)\n- Selected variant pricing adjustments\n- Selected option value extra pricing\n- Quantity multiplier\n\n**Required fields:**\n- productId: UUID of the catalog product to import\n- quantity: Minimum 1\n- options: Array of option selections (can be empty array)\n\n**Optional fields:**\n- activeVariantId: UUID of the variant to activate\n\nReturns the ID of the newly created product in the project.","operationId":"ProjectProductsController_importProductToProject","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportProductToProjectDto"}}}},"responses":{"201":{"description":"Product imported successfully","schema":{"example":{"success":true,"id":"550e8400-e29b-41d4-a716-446655440000"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad Request - Invalid input or validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Catalog product or project does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Import product from catalog","tags":["projects","products"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/products/{productId}":{"get":{"description":"Returns full product details including variants, options, and HTML-formatted description.\n\nThis endpoint provides complete information about a product in a project, including:\n\n**Product Information:**\n- Basic details (name, description as HTML, category, logo)\n- Pricing structure (fixed price, subscription price, per-unit price)\n- Price calculation details (unit name, frequency, final calculated prices)\n- Quantity and active date ranges\n\n**Variants:**\n- All available product variants (e.g., Standard, Pro, Enterprise)\n- Each variant includes its own pricing and HTML-formatted description\n- Active variant status and sort order\n\n**Options:**\n- All available product options (add-ons or configuration choices)\n- Option values with extra pricing adjustments\n- Required/multiple selection settings\n\nThe description and variant descriptions are returned as HTML, converted from the internal document format.","operationId":"ProjectProductsController_getProductDetails","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}},{"name":"productId","required":true,"in":"path","description":"Product ID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Product details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectProductDetailsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Product or project does not exist"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get product details","tags":["projects","products"],"x-required-scopes":["api:read"]},"delete":{"description":"Soft deletes a product from a project by setting the deletedAt timestamp. The product is not permanently removed, but is hidden from product lists and excluded from calculations.\n\n**Soft Delete Behavior:**\n- Product is marked as deleted with a timestamp\n- Product no longer appears in list endpoints\n- Product is excluded from price calculations\n- Historical data is preserved for audit purposes\n\n**Note:** This only removes the product from the project. The original catalog product remains unchanged.","operationId":"ProjectProductsController_deleteProduct","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}},{"name":"productId","required":true,"in":"path","description":"Product ID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Product deleted successfully","schema":{"example":{"success":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Product or project does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete product from project","tags":["projects","products"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/products/{productId}/settings":{"post":{"description":"Updates only product configuration settings without modifying product details. This endpoint is designed for quick updates to how a product is used in a project, without changing the product itself.\n\n**What gets updated:**\n- quantity: How many units of this product are in the project\n- activeVariantId: Which variant is currently active (e.g., Standard vs Pro)\n- options: Which option values are selected (e.g., color, add-ons)\n\n**What does not get updated:**\n- Product name, description, category, or logo\n- Base pricing (priceFixed, priceSubscription, pricePerUnit)\n- Variant definitions or option definitions\n- Active date ranges\n\n**Use cases:**\n- Customer changes their mind about variant selection\n- Adjusting quantity after initial import\n- Adding or removing optional add-ons\n- Quick configuration changes without full product edit\n\n**Pricing Impact:**\nChanging quantity, variant, or options will affect the final calculated prices, but the base product pricing structure remains unchanged.","operationId":"ProjectProductsController_updateProductSettings","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}},{"name":"productId","required":true,"in":"path","description":"Product ID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductSettingsDto"}}}},"responses":{"200":{"description":"Product settings updated successfully","schema":{"example":{"success":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad Request - Invalid input or validation error"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Product or project does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update product settings","tags":["projects","products"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/products/{productId}/duplicate":{"post":{"description":"Creates a copy of an existing product within the same project. Useful when you need the same product multiple times with different configurations or for different time periods.\n\n**What gets duplicated:**\n- All product details (name, description, category, logo)\n- All pricing information\n- All variants and their configurations\n- All options and option values\n- Current quantity and settings\n\n**Use cases:**\n- Same product with different active date ranges\n- Same product with different variant/option selections\n- Multiple instances of a product in one project\n\nReturns the ID of the newly created duplicate product.","operationId":"ProjectProductsController_duplicateProduct","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}},{"name":"productId","required":true,"in":"path","description":"Product ID to duplicate","schema":{"format":"uuid","type":"string"}}],"responses":{"201":{"description":"Product duplicated successfully","schema":{"example":{"success":true,"id":"550e8400-e29b-41d4-a716-446655440001"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Product or project does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Duplicate product","tags":["projects","products"],"x-required-scopes":["api:write"]}},"/projects/{projectId}/products/sort":{"post":{"description":"Updates the sort order for multiple products in a project. Products are displayed in the order specified by the provided array of product IDs.\n\n**How it works:**\n1. Provide an array of product IDs in the desired display order\n2. The first ID in the array will have the lowest sort value\n3. Subsequent IDs receive incrementally higher sort values\n4. Products not included in the array retain their current sort order\n\n**Requirements:**\n- All product IDs must belong to the specified project\n- All product IDs must be valid UUIDs\n- Array must contain at least one product ID\n\n**Use cases:**\n- Reorganizing product list for better presentation\n- Grouping related products together\n- Prioritizing important products at the top","operationId":"ProjectProductsController_reorderProducts","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductSortDto"}}}},"responses":{"200":{"description":"Products reordered successfully","schema":{"example":{"success":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad Request - Invalid input"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient permissions or no project access"},"404":{"description":"Not Found - Project does not exist"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder products","tags":["projects","products"],"x-required-scopes":["api:write"]}},"/projects/{id}/versions":{"get":{"description":"Returns a list of all saved project versions (snapshots) with their details.\n\n**What are Project Versions?**\nProject versions freeze the current state of a project configuration at a specific point in time. They capture the complete project state including:\n- Project tree structure with dynamically activated work packages\n- Products including variants, prices, and discounts\n- Answered form questions and their effects\n- Manually added items\n- All configuration changes\n\nVersions ensure that offered, negotiated, and contracted content remains traceable. They serve as the basis for quotes, specifications, and billing documents.\n\n**What is returned:**\n- **id**: Unique version identifier (UUID)\n- **title**: Version title/name\n- **semverVersion**: Semantic version string (e.g., \"1.2.3\")\n- **branchId**: Branch identifier for version tracking\n- **parentId**: Parent version ID (null for first version)\n- **createdAt**: ISO 8601 timestamp when version was created\n- **createdBy**: User ID who created the version\n- **description**: HTML-formatted version description\n- **isCurrent**: Whether this is the current active version for the project\n\n**Use cases:**\n- View version history to see how project evolved\n- Select a version when creating quotes or specifications\n- Compare different project states\n- Restore to a previous version if needed","operationId":"ProjectVersionsController_listVersions","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Array of project versions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectVersionDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all versions for a project","tags":["projects","versions"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new version (snapshot) of the current project configuration state.\n\n**What gets saved:**\nThe version captures the complete project state at the time of creation:\n- Project tree structure with all components and work packages\n- Products including variants, prices, and discounts\n- Answered form questions and their effects\n- Manually added items\n- All configuration changes\n\n**How to use:**\n1. Make changes to your project configuration\n2. Call this endpoint with a descriptive title\n3. Optionally provide a description in HTML or Markdown format\n4. The version is created and becomes the current active version\n\n**Best practices:**\n- Create versions after filling out questionnaires or making structural changes\n- Create a version before sending out offers\n- Use clear, descriptive version titles\n- Create variants when customers want to compare alternatives\n\n**Required permissions:** Projects.manageSnapshots permission is required to create versions.","operationId":"ProjectVersionsController_createVersion","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectVersionDto"}}}},"responses":{"200":{"description":"Created version with description as HTML","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectVersionDto"}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new project version","tags":["projects","versions"],"x-required-scopes":["api:write"]}},"/projects/{id}/versions/{versionId}":{"get":{"description":"Returns complete details for a specific project version, including estimate data.\n\n**What is returned:**\n- All standard version fields (id, title, semverVersion, branchId, parentId, createdAt, createdBy, description, isCurrent)\n- **estimateData**: Complete estimate data including:\n  - Products with variants, prices, and discounts\n  - Components with nested structure\n  - Manual positions\n  - Pricing breakdowns (fixed, subscriptions, per-unit)\n\n**Use cases:**\n- View detailed version information\n- Access estimate data for a specific version\n- Compare estimate data between versions\n- Use version data for generating documents or reports","operationId":"ProjectVersionsController_getVersionDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"versionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Version details with estimate data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectVersionDetailsDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Version or project not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get version details","tags":["projects","versions"],"x-required-scopes":["api:read"]}},"/projects/{id}/versions/{versionId}/restore":{"post":{"description":"Restores the project configuration to the state of a previously saved version. This is a destructive operation that replaces the current project state.\n\n**What happens:**\n- The current project configuration is replaced with the selected version\n- All current components, products, and manual positions are removed\n- The project state is restored to exactly match the saved version\n- The restored version becomes the current active version\n\n**Warning:**\nThis operation cannot be undone. All unsaved changes will be lost. Make sure to create a new version of the current state before restoring if you want to preserve it.\n\n**Use cases:**\n- Rollback to a previous version after unwanted changes\n- Restore a project variant for comparison\n- Revert to a stable configuration\n\n**Required permissions:** Projects.manageSnapshots permission is required to restore versions.","operationId":"ProjectVersionsController_restoreVersion","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"versionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Version restored successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Insufficient permissions"},"404":{"description":"Version or project not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Restore project to a previous version","tags":["projects","versions"],"x-required-scopes":["api:write"]}},"/projects/{id}/configuration-state":{"get":{"description":"Returns the current state of the project configuration, including version information and change tracking.\n\n**What is returned:**\n- **lastConfigurationSnapshot**: Information about the last saved version (id, version, createdAt, title)\n- **configurationChanged**: Whether the project configuration has changed since the last version was saved\n- **configurationChangedAt**: ISO 8601 timestamp when configuration last changed (null if unchanged)\n- **productCount**: Number of products in the project\n- **componentCount**: Number of components in the project\n- **manualPositionCount**: Number of manual positions in the project\n- **discountCount**: Number of discounts applied\n- **snapshotCount**: Total number of versions saved for the project\n- **documentCount**: Number of documents associated with the project\n\n**Use cases:**\n- Check if there are unsaved changes (configurationChanged flag)\n- Get overview of project contents\n- Determine if a new version should be created\n- View version history count","operationId":"ProjectConfigurationStateController_getConfigurationState","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Project configuration state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectConfigurationStateDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get current project configuration state","tags":["projects","versions"],"x-required-scopes":["api:read"]}},"/projects":{"get":{"description":"Returns a paginated, filterable list of all projects in the workspace.\n\n**What are Projects?**\nProjects are the central organizational unit in Leadtime. Every task, time booking, and activity is associated with a project. Projects can be either internal (company activities) or external (client work), and can be structured as single projects (one-off initiatives) or ongoing projects (continuous collaboration).\n\n**Project Types:**\n- **Single Projects**: Clear-cut initiatives with defined start and finish, usually planned and billed as quoted\n- **Ongoing Projects**: Continuous activities where tasks are handled and billed based on effort\n\n**Project Classification:**\n- **External Projects**: Billable customer projects that generate direct revenue\n- **Internal Projects**: Non-billable activities essential for running the company (e.g., administration, product development)\n\n**Value Groups:**\nProjects are classified by value creation:\n- **A - Direct value creation**: External customer projects\n- **B - Indirect value creation**: Internal projects that increase product/service value\n- **C - Not value creating**: Administrative tasks\n- **D - Waste**: Unproductive time (downtime, failed projects)\n\n**What is returned:**\nThe response includes project details such as name, short name (unique identifier), organization, category, status, phase, open tasks count, value group, type, tags, access permissions, and timestamps.\n\nRetrieves a paginated grid of projects with filtering and sorting capabilities. Quick search available on: name, shortName, orgName. Filterable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt. Sortable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt.","operationId":"ProjectsController_listProjects","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: name, shortName, orgName","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Project ID\n- **name** (string): Project name\n- **shortName** (string): Project short name (organization prefix + number)\n- **type** (set): Project type\n- **valueGroup** (set): Value group classification\n- **categoryId** (string): Project category ID\n- **statusId** (string): Project status ID\n- **phaseId** (string): Project phase ID\n- **organizationId** (string): Organization ID\n- **orgName** (string): Organization name\n- **guestAccess** (boolean): Whether guest access is enabled\n- **isFavorite** (boolean): Whether the project is marked as favorite by the current user\n- **isArchived** (boolean): Whether the project is archived\n- **openTasksCount** (number): Number of open tasks\n- **createdAt** (date): Creation timestamp\n- **editedAt** (date): Last update timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **name** (string): Project name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **shortName** (string): Project short name (organization prefix + number) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Project type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **valueGroup** (set): Value group classification (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **categoryId** (string): Project category ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **statusId** (string): Project status ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **phaseId** (string): Project phase ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **orgName** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **guestAccess** (boolean): Whether guest access is enabled (boolean filter). Available comparisons: equal, not_equal\n- **isFavorite** (boolean): Whether the project is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal\n- **isArchived** (boolean): Whether the project is archived (boolean filter). Available comparisons: equal, not_equal\n- **openTasksCount** (number): Number of open tasks (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Project ID\n- **name**: Project name\n- **shortName**: Project short name (organization prefix + number)\n- **type**: Project type\n- **valueGroup**: Value group classification\n- **categoryId**: Project category ID\n- **statusId**: Project status ID\n- **phaseId**: Project phase ID\n- **organizationId**: Organization ID\n- **orgName**: Organization name\n- **guestAccess**: Whether guest access is enabled\n- **isFavorite**: Whether the project is marked as favorite by the current user\n- **isArchived**: Whether the project is archived\n- **openTasksCount**: Number of open tasks\n- **createdAt**: Creation timestamp\n- **editedAt**: Last update timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, name, shortName, icon, type, valueGroup, categoryId, statusId, phaseId, organizationId, orgName, orgColor, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt","schema":{"example":"id,name","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of projects"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List projects","tags":["projects"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new project in the workspace.\n\n**Project Types:**\n- **Single Projects**: One-off initiatives with defined start and finish\n- **Ongoing Projects**: Continuous activities with recurring tasks\n\n**Internal vs External:**\n- **External Projects**: Set organizationId to link to a client organization. These are billable customer projects.\n- **Internal Projects**: Set organizationId to null. These are non-billable company activities.\n\n**Required Fields:**\n- name: Project name\n- type: Project type (Single or Ongoing)\n- valueGroup: Value classification (A-D)\n- categoryId: Project category UUID\n- statusId: Project status UUID\n- users: Array of user IDs (at least one)\n- taskTypes: Array of task type IDs (at least one)\n- activities: Array of activity IDs (at least one)\n\n**Optional Fields:**\n- phaseId: Required for external single projects, optional otherwise\n- organizationId: Required for external projects, must be null for internal projects\n- description: HTML or Markdown (will be converted to internal format)\n- icon: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:shopping_cart:`, `:mobile_phone:`). Use standard emoji short names or custom icon names.\n- deadline: ISO 8601 date string\n- defaultAccountableId: Default accountable user UUID\n- responsibleId: Responsible user UUID\n- teams: Array of team IDs\n- customFields: Key-value pairs for custom data\n- tags: Array of tag strings\n- guestAccess: Whether organization members can access (only for external projects)\n\n**Description Format:**\nThe description field accepts HTML or Markdown and will be automatically converted to the internal IDoc format. When retrieved later, it will be returned as HTML.","operationId":"ProjectsController_createProject","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"name":["Name is required"],"type":["Type is required"],"organizationId":["organizationId is required for client projects"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create project","tags":["projects"],"x-required-scopes":["api:write"]}},"/projects/categories":{"get":{"description":"Returns a list of all project categories configured in the workspace.\n\n**What are Project Categories?**\nCategories classify projects by functional area or project type (e.g., Onboarding, Support, Implementation, Website Development). They help organize and filter projects in the workspace. Categories can be configured in workspace settings and support multi-language translations.\n\n**What is returned:**\nEach category includes:\n- Unique identifier (ID)\n- Name and description\n- Icon identifier\n- Multi-language translations (if configured)\n\nThis is a read-only endpoint optimized for common list data retrieval.","operationId":"ProjectsController_listCategories","parameters":[],"responses":{"200":{"description":"Project categories retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectCategoryDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all project categories","tags":["projects","common-lists"],"x-required-scopes":["api:read"]}},"/projects/statuses":{"get":{"description":"Returns a list of all project statuses configured in the workspace.\n\n**What are Project Statuses?**\nStatuses represent the current progress stage of a project (e.g., Sales, Implementation, Billing, Done). They help track project lifecycle and workflow. Statuses can be configured in workspace settings and support multi-language translations.\n\n**Status Types:**\nEach status has a type that determines its behavior in the project workflow.\n\n**What is returned:**\nEach status includes:\n- Unique identifier (ID)\n- Name and description\n- Icon identifier\n- Status type\n- Multi-language translations (if configured)\n\nThis is a read-only endpoint optimized for common list data retrieval.","operationId":"ProjectsController_listStatuses","parameters":[],"responses":{"200":{"description":"Project statuses retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectStatusDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all project statuses","tags":["projects","common-lists"],"x-required-scopes":["api:read"]}},"/projects/phases":{"get":{"description":"Returns a list of all project phases configured in the workspace.\n\n**What are Project Phases?**\nPhases represent distinct stages or steps within a project lifecycle (e.g., Planning, Development, Testing, Deployment). They provide additional granularity beyond project statuses and are particularly useful for external single projects. Phases can be configured in workspace settings and support multi-language translations.\n\n**Phase Types:**\nEach phase has a type that determines its behavior in the project workflow.\n\n**What is returned:**\nEach phase includes:\n- Unique identifier (ID)\n- Name and description\n- Icon identifier\n- Phase type\n- Multi-language translations (if configured)\n\nThis is a read-only endpoint optimized for common list data retrieval.","operationId":"ProjectsController_listPhases","parameters":[],"responses":{"200":{"description":"Project phases retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectPhaseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all project phases","tags":["projects","common-lists"],"x-required-scopes":["api:read"]}},"/projects/{id}/component-overview":{"get":{"description":"Returns the hierarchical structure of project components, items, and associated tasks.\n\n**What are Components?**\nComponents are reusable project templates that standardize recurring project types. They serve as blueprints for planning, pricing, and executing similar projects consistently. When imported into a project, components create a structured tree of work items.\n\n**Component Structure:**\nComponents contain a hierarchical tree of items:\n- **Epics**: Groups related work packages around a common theme\n- **Work Packages**: Self-contained, clearly defined tasks that deliver tangible results\n- **Todo Lists**: Checklists for quality control and process steps\n- **Test Suites**: Acceptance tests for formal approval\n\n**What is returned:**\nThe overview shows:\n- Complete hierarchical structure of all components in the project\n- All component items (Epics, Work Packages, Todo Lists, Test Suites)\n- Tasks that have been connected to component items\n- Implementation progress and status information\n\nThis endpoint helps track how project components are being implemented and which tasks are associated with each component item.","operationId":"ProjectsController_getComponentOverview","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Component overview retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - missing required permission"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project component overview","tags":["projects"],"x-required-scopes":["api:read"]}},"/projects/{id}/objects":{"get":{"description":"Returns objects whose current projectId matches this project. Object visibility rules still apply.","operationId":"ProjectsController_listProjectObjects","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":200,"default":50,"type":"number"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List objects assigned to a project","tags":["projects"],"x-required-scopes":["api:read"]}},"/projects/{id}":{"get":{"description":"Returns complete project details including all settings, relationships, and metadata.\n\n**What is returned:**\nThe response includes:\n- Basic information: name, short name, type, value group, description, icon\n- Classification: category, status, phase, organization\n- Access settings: guest access, assigned users and teams\n- Task configuration: enabled task types and activities\n- Custom data: custom fields, tags\n- Responsibilities: default accountable user, responsible user\n- Timestamps: creation date, last edit date, archive date\n- Project metadata: color, favorite status, express quotations setting\n\nThe description field is returned as HTML (converted from the internal IDoc format).","operationId":"ProjectsController_getProjectDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get project details","tags":["projects"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates only the fields provided in the request body. All fields are optional - only included fields will be updated.\n\n**How it works:**\n- Send only the fields you want to update\n- Fields not included in the request remain unchanged\n- Arrays (users, teams, taskTypes, activities, tags) replace the entire array\n- Set a field to null to clear it (where applicable)\n\n**Description Format:**\nIf you provide a description field, it accepts HTML or Markdown and will be converted to the internal IDoc format.\n\n**Important Notes:**\n- organizationId: Can be changed, but must be set for external projects and null for internal projects\n- phaseId: Can be updated, but is required for external single projects\n- enableExpressQuotations: Only applicable for external projects\n- guestAccess: Only applicable for external projects","operationId":"ProjectsController_patchProject","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update project","tags":["projects"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a project. The project will be marked as deleted (archived) but not permanently removed from the database.\n\n**What happens:**\n- The project is marked as archived\n- It will no longer appear in active project lists\n- Historical data (tasks, time bookings, etc.) is preserved\n- The project can be viewed in archived projects if needed\n\n**Note:** This is a soft delete operation. The project data remains in the database for historical reporting and audit purposes.","operationId":"ProjectsController_deleteProject","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete project","tags":["projects"],"x-required-scopes":["api:write"]}},"/projects/{id}/active-subscriptions":{"get":{"description":"Returns all active subscriptions (manual and product-based) for a project as of the current date.\n\n**What are Active Subscriptions?**\nActive subscriptions combine:\n- **Manual subscriptions**: Fixed price and variable price subscriptions configured directly for the project\n- **Product subscriptions**: Products with subscription pricing from the billing version/snapshot (for Single projects) or current products (for Support projects)\n\n**Project Type Behavior:**\n- **Single Projects**: Uses products from the billing version/snapshot (productsBillingProjectSnapshotId or billingProjectSnapshotId)\n- **Support Projects**: Uses current products (no snapshot)\n\n**What is returned:**\n- Only subscriptions that are currently active (activeFrom <= now <= activeTo)\n- Each subscription includes: name, activeFrom date, activeTo date, priceSubscription, pricePerUnit\n- Manual subscriptions: name comes from manualTitle, dates from fromDate/toDate\n- Product subscriptions: name comes from finalName, dates from activeFrom/activeTo\n\n**Note:** Requires project access permission.","operationId":"ProjectsController_getActiveSubscriptions","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Active subscriptions retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found or access denied"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get active subscriptions for a project","tags":["projects"],"x-required-scopes":["api:read"]}},"/projects/{id}/support-contingents":{"get":{"description":"Retrieves all support contingents (retainer plans) for a specific project with detailed information including available and consumed hours.\n\n**What are Support Contingents?**\nSupport contingents are retainer plans that define allocated hours and pricing for support periods. They help manage support contracts with predefined hours, rates, and transferability of unused hours.\n\n**What is returned:**\n- All support contingents for the project\n- Each contingent includes: title, date range, frequency, hours, transferability, active status\n- For active contingents: available hours and consumed hours for the current period\n- Available hours calculation considers transferable logic and consumed hours from invoices\n- Results are sorted by active status (active first) and then by fromDate\n\n**Note:** Requires project access permission.","operationId":"ProjectsController_getSupportContingentsWithDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Support contingents retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportContingentWithDetailsResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List support contingents with details for a project","tags":["projects","projects","support-contingents"],"x-required-scopes":["api:read"]}},"/projects/{id}/support-contingents-details":{"get":{"description":"Retrieves a detailed period-by-period breakdown of all support contingents for a specific project.\n\n**What are Support Contingents?**\nSupport contingents are retainer plans that define allocated hours and pricing for support periods. They help manage support contracts with predefined hours, rates, and transferability of unused hours.\n\n**What is returned:**\n- All support contingents for the project\n- For each contingent: period-by-period breakdown from start date until present day\n- Each period includes: start/end dates, allocated hours, hours from previous period (if transferable), consumed hours, and list of tasks\n- Tasks include: title, short number, hours billed, invoice number (if billed), and unbilled flag (if in progress)\n- Current period includes unbilled tasks with their spent hours\n- Periods are sorted newest first, active contingents appear before inactive ones\n- Tasks within each period are sorted with unbilled tasks first, then by short number\n\n**Note:** Requires project access and `Projects.canSeeSupportContingentDetails` permission.","operationId":"ProjectsController_getSupportContingentsBreakdown","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Support contingents breakdown retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportContingentWithBreakdownResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get detailed period-by-period breakdown of support contingents","tags":["projects","projects","support-contingents"],"x-required-scopes":["api:read"]}},"/object-types":{"get":{"operationId":"ObjectTypesController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectTypeListItemDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List object types (admin)","tags":["object-types"],"x-required-scopes":["api:read"]},"post":{"operationId":"ObjectTypesController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateObjectTypeDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create object type (admin)","tags":["object-types"],"x-required-scopes":["api:write"]}},"/object-types/sort":{"post":{"operationId":"ObjectTypesController_saveObjectTypeSort","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSortDTO"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder object types (admin)","tags":["object-types"],"x-required-scopes":["api:write"]}},"/object-types/{typeId}/statuses":{"get":{"operationId":"ObjectTypesController_listStatuses","parameters":[{"name":"typeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectStatusDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List statuses for an object type (admin)","tags":["object-types"],"x-required-scopes":["api:read"]},"post":{"operationId":"ObjectTypesController_createStatus","parameters":[{"name":"typeId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateObjectStatusDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectStatusDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create object status (admin)","tags":["object-types"],"x-required-scopes":["api:write"]}},"/object-types/{typeId}/statuses/sort":{"post":{"operationId":"ObjectTypesController_saveStatusSort","parameters":[{"name":"typeId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSortDTO"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder statuses for an object type (admin)","tags":["object-types"],"x-required-scopes":["api:write"]}},"/object-types/{typeId}/statuses/{statusId}":{"put":{"operationId":"ObjectTypesController_updateStatus","parameters":[{"name":"typeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"statusId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateObjectStatusDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectStatusDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update object status (admin)","tags":["object-types"],"x-required-scopes":["api:write"]},"delete":{"operationId":"ObjectTypesController_deleteStatus","parameters":[{"name":"typeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"statusId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete object status (admin, soft delete)","tags":["object-types"],"x-required-scopes":["api:write"]}},"/object-types/{id}":{"get":{"operationId":"ObjectTypesController_getOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectTypeDetailResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get object type with statuses and custom fields (admin)","tags":["object-types"],"x-required-scopes":["api:read"]},"put":{"operationId":"ObjectTypesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateObjectTypeDto"}}}},"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update object type (admin)","tags":["object-types"],"x-required-scopes":["api:write"]},"delete":{"operationId":"ObjectTypesController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete object type (admin, soft delete). Allowed even when object instances exist; instances keep objectTypeId pointing at the soft-deleted type.","tags":["object-types"],"x-required-scopes":["api:write"]}},"/objects":{"get":{"operationId":"ObjectsController_list","parameters":[{"name":"objectTypeId","required":false,"in":"query","description":"Filter by object type UUID","schema":{"type":"string"}},{"name":"organizationId","required":false,"in":"query","description":"Filter by organization UUID","schema":{"type":"string"}},{"name":"projectId","required":false,"in":"query","description":"Filter by assigned project UUID","schema":{"type":"string"}},{"name":"statusId","required":false,"in":"query","description":"Filter by object status UUID","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","description":"Case-insensitive substring match on name","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":200,"default":50,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedObjectsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List objects (visibility rules apply)","tags":["objects"],"x-required-scopes":["api:read"]},"post":{"operationId":"ObjectsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateObjectDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectInstanceResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create object","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}/files":{"get":{"operationId":"ObjectsController_listFiles","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectFileResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List files attached to an object","tags":["objects"],"x-required-scopes":["api:read"]},"post":{"description":"Upload via POST /workspace/upload first, then pass fileId here.","operationId":"ObjectsController_attachFile","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachObjectFileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectFileResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Attach a file to an object","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}/files/{objectFileId}":{"delete":{"description":"objectFileId is the ObjectFile join row id (not the raw File id).","operationId":"ObjectsController_deleteFile","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"objectFileId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Remove an object file attachment","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}/files/{fileRowId}/download":{"get":{"description":"fileRowId is the ObjectFile join row id (same as in list files). Streams the file as an attachment.","operationId":"ObjectsController_downloadObjectFile","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"fileRowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Download a file attached to an object","tags":["objects"],"x-required-scopes":["api:read"]}},"/objects/file-categories":{"get":{"operationId":"ObjectsController_listFileCategories","parameters":[],"responses":{"200":{"description":"Category id and name pairs","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List object file categories (workspace-wide)","tags":["objects"],"x-required-scopes":["api:read"]}},"/objects/file-categories/save":{"post":{"operationId":"ObjectsController_saveFileCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Set to update"},"name":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update an object file category","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/file-categories/{categoryId}":{"delete":{"operationId":"ObjectsController_deleteFileCategory","parameters":[{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Soft-delete an object file category","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/assignments/assign-to-organization":{"post":{"operationId":"ObjectsController_assignToOrganization","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignObjectsToOrganizationDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Assign objects to an organization","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/assignments/unassign-from-organization":{"post":{"operationId":"ObjectsController_unassignFromOrganization","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnassignObjectFromOrganizationDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Unassign an object from its organization","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/assignments/assign-to-project":{"post":{"operationId":"ObjectsController_assignToProject","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignObjectsToProjectDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Assign objects to a project","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/assignments/unassign-from-project":{"post":{"operationId":"ObjectsController_unassignFromProject","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnassignObjectFromProjectDto"}}}},"responses":{"201":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Unassign an object from its project","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}/journal/{entryId}":{"get":{"operationId":"ObjectsController_getJournalEntry","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectJournalResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get one object journal entry","tags":["objects"],"x-required-scopes":["api:read"]},"put":{"description":"Requires canEditObject, or editing your own entry when you only have canViewObjects (same rules as app).","operationId":"ObjectsController_patchJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchObjectJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectJournalResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update object journal entry","tags":["objects"],"x-required-scopes":["api:write"]},"delete":{"operationId":"ObjectsController_deleteJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete object journal entry (soft delete)","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}/journal":{"get":{"operationId":"ObjectsController_listJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List journal entries for an object (paginated grid)","tags":["objects"],"x-required-scopes":["api:read"]},"post":{"operationId":"ObjectsController_createJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateObjectJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectJournalResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create object journal entry","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}/history":{"get":{"operationId":"ObjectsController_historyForObject","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedObjectHistoryResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Audit history for an object (paginated, newest first)","tags":["objects"],"x-required-scopes":["api:read"]}},"/objects/{id}/tasks":{"get":{"operationId":"ObjectsController_tasksForObject","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TasksByObjectResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List tasks linked to an object","tags":["objects"],"x-required-scopes":["api:read"]}},"/objects/tasks/link":{"post":{"operationId":"ObjectsController_linkObjectToTask","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkObjectToTaskPublicDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkObjectToTaskPublicResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Link an object to a task","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/tasks/link-batch":{"post":{"description":"Creates any missing links; already-linked pairs are skipped. Task-side and object-side audit entries match the internal batch semantics.","operationId":"ObjectsController_linkObjectsBatchToTask","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkObjectsBatchToTaskPublicDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkObjectsBatchPublicResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Link multiple objects to one task","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/tasks/unlink":{"post":{"operationId":"ObjectsController_unlinkObjectFromTask","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkObjectToTaskPublicDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Remove the link between an object and a task","tags":["objects"],"x-required-scopes":["api:write"]}},"/objects/{id}":{"get":{"operationId":"ObjectsController_getOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectInstanceResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get object by id","tags":["objects"],"x-required-scopes":["api:read"]},"put":{"operationId":"ObjectsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateObjectDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectInstanceResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update object","tags":["objects"],"x-required-scopes":["api:write"]},"delete":{"operationId":"ObjectsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Soft-delete object","tags":["objects"],"x-required-scopes":["api:write"]}},"/administration/document-settings":{"get":{"description":"Retrieves the current document settings for the workspace.\n\n**What are Document Settings?**\nDocument settings control the appearance and formatting of all documents automatically generated by Leadtime, including letters, invoices, quotes, specifications, and project documents. These settings apply workspace-wide and can be overridden at the organization or project level.\n\n**What is returned:**\n- **Document font**: The selected font family for all documents (e.g., Inter, Roboto, Arimo)\n- **Document logo URL**: Public URL to the workspace logo image (null if not set, defaults to Leadtime logo)\n- **Project document options**:\n  - Title page toggle (automatically adds a title page)\n  - Table of contents toggle (creates TOC at document start)\n  - Heading style (controls how headings are numbered and formatted)\n- **Background image URLs**: Public URLs for background images used on cover pages:\n  - Specification background (for specs documents)\n  - Estimate background (for proposal/quote documents)\n  - Custom editor background (for template-based documents)\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. All file URLs are public and can be accessed without authentication.","operationId":"DocumentSettingsController_getDocumentSettings","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetDocumentSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get document settings","tags":["administration","document-settings"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates document settings for the workspace. All fields are optional - only provided fields will be updated.\n\n**What are Document Settings?**\nDocument settings control the appearance and formatting of all documents automatically generated by Leadtime, including letters, invoices, quotes, specifications, and project documents.\n\n**How to update document settings:**\n1. **For file uploads** (logo, background images):\n   - Upload image files using POST /api/public/workspace/upload\n   - Get the file ID from the upload response\n   - Use the file ID in the corresponding field (`documentLogoId`, `specificationBackgroundId`, etc.)\n   - To remove a file, set the field to `null`\n2. **For font selection**: Choose from available fonts (Inter, Degular, Roboto, Arimo, Open Sans, PT Sans, Oswald, EB Garamond, Permanent Marker, IBM Plex Mono)\n3. **For project document options**: Set boolean flags and heading style enum values\n\n**Available heading styles:**\n- `Normal`: Headings without numbering\n- `Sequential`: Continuous numbering from level 1 (e.g., 1., 1.1., 1.1.1.)\n- `SequentialFromSecondLevel`: Top heading unnumbered, subchapters numbered (e.g., 1. Heading 2, 1.1. Heading 3)\n- `SequentialWithParagraphs`: Numbering with § prefix from level 2 (e.g., § 1., § 1.1.) - ideal for contracts\n\n**Background image recommendations:**\n- Recommended size: 820 × 600 pixels\n- Formats: PNG, JPG, or SVG\n- Used on cover pages of generated documents\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect all documents generated after the update. Use GET endpoint to preview current settings.","operationId":"DocumentSettingsController_updateDocumentSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchDocumentSettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update document settings","tags":["administration","document-settings"],"x-required-scopes":["api:write"]}},"/sales/estimates/grid":{"get":{"description":"**What are Sales Estimates?**\nSales Estimates provides a unified overview of all ongoing offers and sales opportunities in Leadtime. This endpoint combines two types of estimates into a single grid view:\n- **Project Documents**: Detailed estimates linked to larger projects, created through the project planning process with products, modules, and custom items\n- **Express Quotations**: Quick estimates created directly from tickets for smaller, ad-hoc customer requests\n\n**What data is returned:**\nThe response includes a paginated grid of estimate items with comprehensive pricing information:\n- Each item represents either a project document estimate or an express quotation\n- Only the latest estimate per project or task is returned (older versions are excluded)\n- Pricing breakdown includes fixed prices, subscription prices, and per-unit prices\n- Aggregated totals across all filtered results are provided at the response level\n\n**Pricing components:**\nEach estimate can have three types of pricing:\n- **Fixed Price**: One-time charges for services or products\n- **Subscriptions**: Recurring prices (monthly, quarterly, semi-annually, or annually)\n- **Per Unit**: Prices based on quantity (e.g., price per user or workspace)\n\nThe response includes both individual item pricing and aggregated totals (totalPriceFixed, totalPriceSubscriptions, totalPricePerUnit) across all filtered results.\n\n**Estimate types:**\n- **PROJECT_DOCUMENT**: Estimates created as part of project planning, typically more detailed and structured\n- **EXPRESS_QUOTATION**: Quick estimates created from tickets, ideal for small add-on tasks or support services\n\n**Estimate statuses:**\n- **Draft**: Estimate is being prepared and has not been sent\n- **WaitingForApproval**: Estimate is pending internal approval before sending\n- **Final**: Estimate has been finalized and approved\n- **Pending**: Express quotation has been sent to customer, awaiting response\n- **Accepted**: Customer has accepted the estimate\n- **Rejected**: Customer has rejected the estimate\n- **Parked**: Estimate has been set aside temporarily\n\n**Access restrictions:**\n- This endpoint is restricted to employees only. Organization members (external contacts) cannot access it\n- Results are automatically filtered by the user's project access permissions\n- Only estimates for projects the user has access to are returned\n\n**Supported operations:**\n- Filter by type, organization, project, task, status, pricing components, creator, and creation date\n- Sort by any field including title, organization, project, status, pricing, and dates\n- Quick search across estimate titles\n- Pagination with configurable page size\n- Field selection to return only specific fields in the response\n\n**Use cases:**\n- Generate sales pipeline reports showing all active estimates\n- Track revenue potential across different estimate types\n- Monitor estimate status distribution (draft, pending, accepted, etc.)\n- Analyze pricing breakdowns by organization or project\n- Export estimate data for external reporting tools\n\nRetrieves a paginated grid of estimates with filtering and sorting capabilities. Quick search available on: title, displayTitle. Filterable fields: id, type, title, displayTitle, organizationId, projectId, taskId, createdAt, status, priceFixed, priceSubscriptions, pricePerUnit, createdById, offerNumber. Sortable fields: id, type, title, displayTitle, organizationId, organizationName, projectId, projectName, taskId, taskShortNumber, createdAt, status, total, priceFixed, priceSubscriptions, pricePerUnit, createdById, createdByName, offerNumber.","operationId":"SalesEstimatesController_getEstimatesGrid","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: title, displayTitle","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Estimate ID\n- **type** (string): Estimate type (PROJECT_DOCUMENT or EXPRESS_QUOTATION)\n- **title** (string): Estimate title\n- **displayTitle** (string): Display title (combined title/offerNumber based on documentStyle)\n- **organizationId** (string): Organization ID\n- **projectId** (string): Project ID\n- **taskId** (string): Task ID (only for EXPRESS_QUOTATION)\n- **createdAt** (date): Creation timestamp\n- **status** (string): Estimate status (Draft, Final, WaitingForApproval, Pending, etc.)\n- **priceFixed** (number): Fixed price\n- **priceSubscriptions** (number): Subscription price\n- **pricePerUnit** (number): Per-unit price\n- **createdById** (string): Creator user ID\n- **offerNumber** (number): Offer number (for PROJECT_DOCUMENT type)\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Estimate ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (string): Estimate type (PROJECT_DOCUMENT or EXPRESS_QUOTATION) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **title** (string): Estimate title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **displayTitle** (string): Display title (combined title/offerNumber based on documentStyle) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **taskId** (string): Task ID (only for EXPRESS_QUOTATION) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **status** (string): Estimate status (Draft, Final, WaitingForApproval, Pending, etc.) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **priceFixed** (number): Fixed price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **priceSubscriptions** (number): Subscription price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **pricePerUnit** (number): Per-unit price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **createdById** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **offerNumber** (number): Offer number (for PROJECT_DOCUMENT type) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Estimate ID\n- **type**: Estimate type (PROJECT_DOCUMENT or EXPRESS_QUOTATION)\n- **title**: Estimate title\n- **displayTitle**: Display title (combined title/offerNumber based on documentStyle)\n- **organizationId**: Organization ID\n- **organizationName**: Organization name\n- **projectId**: Project ID\n- **projectName**: Project name\n- **taskId**: Task ID (only for EXPRESS_QUOTATION)\n- **taskShortNumber**: Task short number (only for EXPRESS_QUOTATION)\n- **createdAt**: Creation timestamp\n- **status**: Estimate status (Draft, Final, WaitingForApproval, Pending, etc.)\n- **total**: Total price (for express quotations)\n- **priceFixed**: Fixed price\n- **priceSubscriptions**: Subscription price\n- **pricePerUnit**: Per-unit price\n- **createdById**: Creator user ID\n- **createdByName**: Creator name\n- **offerNumber**: Offer number (for PROJECT_DOCUMENT type)\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, type, title, displayTitle, organizationId, organizationName, projectId, projectName, taskId, taskShortNumber, createdAt, status, total, priceFixed, priceSubscriptions, pricePerUnit, priceSubscriptionsDetail, pricePerUnitDetail, createdById, createdByName, offerNumber, formattedOfferNumber","schema":{"example":"id,type","type":"array","items":{}}}],"responses":{"200":{"description":"Estimates grid with pricing totals (totalPriceFixed, totalPriceSubscriptions, totalPricePerUnit)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateGridResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get sales estimates grid","tags":["sales","estimates"],"x-required-scopes":["api:read"]}},"/sales/opportunities":{"get":{"description":"Retrieves a paginated grid of sales opportunities with filtering and sorting capabilities. Quick search available on: name, shortName, orgName. Filterable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt. Sortable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt.\n\n**What are Sales Opportunities?**\n\nSales opportunities represent potential projects in the customer lifecycle where a customer has not placed any order yet. They are used to organize leads, track sales stages, manage contacts, and systematically move potential deals forward. In Leadtime, sales opportunities work as projects that are assigned to an external organization (customer). The goal of every opportunity is to create an offer, and if successful, it can turn into an implementation project.\n\n**How Sales Opportunities Work:**\n\n- Sales opportunities are Single-type projects that have an organizationId (external projects)\n- They can be linked to one or more offers or estimates\n- They use project phases to track progress through the sales process (e.g., Uncontacted, Research, Contact made, Reminder, Implementation, Won, Finished)\n- They support custom fields for storing additional information like probability, source, region, or segment\n- They include a journal for documenting activities, feedback, and interactions with potential customers\n\n**Automatic Filtering:**\n\nThis endpoint automatically applies three filters to ensure only relevant sales opportunities are returned:\n1. **Project type**: Must be Single (only single projects can be sales opportunities)\n2. **Organization ID**: Must not be empty (only external projects linked to organizations)\n3. **Status exclusion**: Projects with Done status type are automatically excluded (only active opportunities are shown)\n\n**Data Returned:**\n\nThe response includes project grid items with fields such as:\n- Project identification: id, name, shortName, icon\n- Project classification: type, valueGroup, categoryId, statusId, phaseId\n- Organization information: organizationId, orgName, orgColor\n- Project metadata: createdAt, editedAt, isFavorite, isArchived, guestAccess\n- Task information: openTasksCount\n\n**Access Control:**\n\nUsers can only see opportunities for projects they have access to based on their project permissions. The endpoint requires the Opportunities.manageSalesOpportunities permission.","operationId":"SalesOpportunitiesController_listOpportunities","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: name, shortName, orgName","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Project ID\n- **name** (string): Project name\n- **shortName** (string): Project short name (organization prefix + number)\n- **type** (set): Project type\n- **valueGroup** (set): Value group classification\n- **categoryId** (string): Project category ID\n- **statusId** (string): Project status ID\n- **phaseId** (string): Project phase ID\n- **organizationId** (string): Organization ID\n- **orgName** (string): Organization name\n- **guestAccess** (boolean): Whether guest access is enabled\n- **isFavorite** (boolean): Whether the project is marked as favorite by the current user\n- **isArchived** (boolean): Whether the project is archived\n- **openTasksCount** (number): Number of open tasks\n- **createdAt** (date): Creation timestamp\n- **editedAt** (date): Last update timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **name** (string): Project name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **shortName** (string): Project short name (organization prefix + number) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Project type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **valueGroup** (set): Value group classification (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **categoryId** (string): Project category ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **statusId** (string): Project status ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **phaseId** (string): Project phase ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **orgName** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **guestAccess** (boolean): Whether guest access is enabled (boolean filter). Available comparisons: equal, not_equal\n- **isFavorite** (boolean): Whether the project is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal\n- **isArchived** (boolean): Whether the project is archived (boolean filter). Available comparisons: equal, not_equal\n- **openTasksCount** (number): Number of open tasks (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Project ID\n- **name**: Project name\n- **shortName**: Project short name (organization prefix + number)\n- **type**: Project type\n- **valueGroup**: Value group classification\n- **categoryId**: Project category ID\n- **statusId**: Project status ID\n- **phaseId**: Project phase ID\n- **organizationId**: Organization ID\n- **orgName**: Organization name\n- **guestAccess**: Whether guest access is enabled\n- **isFavorite**: Whether the project is marked as favorite by the current user\n- **isArchived**: Whether the project is archived\n- **openTasksCount**: Number of open tasks\n- **createdAt**: Creation timestamp\n- **editedAt**: Last update timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, name, shortName, icon, type, valueGroup, categoryId, statusId, phaseId, organizationId, orgName, orgColor, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt","schema":{"example":"id,name","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of sales opportunities"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List sales opportunities","tags":["sales"],"x-required-scopes":["api:read"]}},"/administration/subscription-status":{"get":{"description":"**What is Subscription Status?**\nSubscription Status provides comprehensive information about your workspace subscription, billing details, and payment information. This endpoint returns all data needed to display subscription management interfaces, including current plan details, upcoming invoices, payment history, and feature availability.\n\n**What data is returned:**\n\n**Core Subscription Information:**\n- **billingMode**: The workspace billing tier (free, regular, or enterprise)\n- **billingStatus**: Current subscription status (active, trialing, canceled, etc.)\n- **planCode**: The specific plan identifier\n- **billingInterval**: Billing frequency (monthly or yearly)\n- **features**: List of enabled workspace features\n- **featuresInfo**: Detailed feature information with pricing for each enabled feature\n\n**Stripe Integration:**\n- **stripeCustomerId**: Stripe customer identifier (null for free workspaces)\n- **stripeSubscriptionId**: Stripe subscription identifier (null for free workspaces)\n- **hasPaymentMethod**: Whether a payment method is on file\n\n**Trial Information:**\n- **trialEnd**: End date of trial period (null if not in trial)\n\n**Subscription Details** (Enterprise workspaces only):\n- **status**: Current subscription status from Stripe\n- **currentPeriodStart/End**: Current billing period dates\n- **items**: List of subscription items with product names, quantities, and pricing\n- **dashboardUrl**: Direct link to Stripe dashboard for this subscription\n\n**Invoice Information** (Paid workspaces only):\n- **nextInvoice**: Preview of upcoming invoice with:\n  - Line items with descriptions and quantities\n  - Subtotal, tax, and total amounts\n  - Tax breakdown by rate\n  - Due date\n- **latestInvoices**: Up to 5 most recent invoices with:\n  - Invoice number and status\n  - Amount and currency\n  - Payment dates and due dates\n  - Direct links to view invoices\n\n**Seat Management:**\n- **billableUsersCount**: Current number of billable users\n- **committedSeats**: Seats currently committed for billing\n- **pendingSeats**: Seats scheduled to be added in next period\n- **pendingEffectiveAt**: When pending seats become effective\n\n**Cancellation Information:**\n- **cancelAtPeriodEnd**: Whether subscription will cancel at period end\n- **cancellationEffectiveAt**: When cancellation becomes effective\n- **cancellationRequestedAt**: When cancellation was requested\n- **cancellationReason**: Reason provided for cancellation\n\n**Billing Interval Changes:**\n- **nextBillingInterval**: Scheduled billing interval change (if any)\n- **billingIntervalChangeScheduled**: When the change takes effect\n- **stillAvailableFeatures**: Features that remain available after interval change\n\n**Important Notes:**\n- This is a read-only endpoint - it does not modify subscription state\n- Free workspaces return basic information without Stripe details\n- Enterprise workspaces include detailed Stripe subscription information\n- Trial workspaces may have limited invoice history\n- All monetary amounts are in cents (divide by 100 for display)\n- Dates are returned in ISO 8601 format","operationId":"SubscriptionStatusController_getSubscriptionStatus","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStatusResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get workspace subscription and billing status","tags":["administration","subscription-status"],"x-required-scopes":["api:read"]}},"/billing/invoices/new":{"get":{"description":"Returns a paginated, filterable, and sortable grid of invoices that are created but not yet sent, or sent but not yet overdue.\n\n**What are New Invoices?**\nThe \"New\" tab shows all invoices that have been created but not yet sent, or invoices that have been sent but are not yet overdue. These invoices are in status \"New\" or \"Sent\" and have either no due date or a due date in the future.\n\n**What is returned:**\n- Invoice identification (ID, number)\n- Project reference (projectId)\n- Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- Creation and sending dates (createdAt, sentAt)\n- Payment due date (dueDate)\n- Net amount before taxes and fees (netto)\n- Invoice status (New, Sent)\n\n**Filtering and search:**\n- Quick search across invoice number\n- Filter by project, type, status, dates, and amount\n- Sort by any field\n\nRetrieves a paginated grid of new invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, sentAt, dueDate, netto, status. Sortable fields: id, number, projectId, type, createdAt, sentAt, dueDate, netto, status.","operationId":"InvoicesController_getNewInvoices","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: number","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Invoice ID\n- **number** (string): Invoice number\n- **projectId** (string): Project ID\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt** (date): Invoice creation date\n- **sentAt** (date): Date when invoice was sent\n- **dueDate** (date): Payment due date\n- **netto** (number): Net amount (before taxes and fees)\n- **status** (set): Invoice status (New, Sent)\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **sentAt** (date): Date when invoice was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **dueDate** (date): Payment due date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **status** (set): Invoice status (New, Sent) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Invoice ID\n- **number**: Invoice number\n- **projectId**: Project ID\n- **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt**: Invoice creation date\n- **sentAt**: Date when invoice was sent\n- **dueDate**: Payment due date\n- **netto**: Net amount (before taxes and fees)\n- **status**: Invoice status (New, Sent)\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, number, projectId, type, createdAt, sentAt, dueDate, netto, status","schema":{"example":"id,number","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved new invoices grid"},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get new invoices grid","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/invoices/paid":{"get":{"description":"Returns a paginated, filterable, and sortable grid of invoices that have been marked as paid.\n\n**What are Paid Invoices?**\nThe \"Paid\" tab shows all invoices that have been fully settled. These invoices are in status \"Paid\" and represent completed transactions.\n\n**What is returned:**\n- Invoice identification (ID, number)\n- Project reference (projectId)\n- Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- Creation date (createdAt)\n- Net amount before taxes and fees (netto)\n- Invoice status (Paid)\n\n**Filtering and search:**\n- Quick search across invoice number\n- Filter by project, type, status, date, and amount\n- Sort by any field\n\nRetrieves a paginated grid of paid invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, netto, status. Sortable fields: id, number, projectId, type, createdAt, netto, status.","operationId":"InvoicesController_getPaidInvoices","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: number","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Invoice ID\n- **number** (string): Invoice number\n- **projectId** (string): Project ID\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt** (date): Invoice creation date\n- **netto** (number): Net amount (before taxes and fees)\n- **status** (set): Invoice status (Paid)\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **status** (set): Invoice status (Paid) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Invoice ID\n- **number**: Invoice number\n- **projectId**: Project ID\n- **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt**: Invoice creation date\n- **netto**: Net amount (before taxes and fees)\n- **status**: Invoice status (Paid)\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, number, projectId, type, createdAt, netto, status","schema":{"example":"id,number","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved paid invoices grid"},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get paid invoices grid","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/invoices/canceled":{"get":{"description":"Returns a paginated, filterable, and sortable grid of invoices that have been cancelled.\n\n**What are Canceled Invoices?**\nThe \"Canceled\" tab shows all invoices that were officially cancelled after being sent out. Cancellation happens via credit note and balances out the original amount correctly from an accounting point of view.\n\n**What is returned:**\n- Invoice identification (ID, number)\n- Project reference (projectId)\n- Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- Creation date (createdAt)\n- Cancellation date (statusChangedAt)\n- Net amount before taxes and fees (netto)\n- Invoice status (Cancelled)\n\n**Filtering and search:**\n- Quick search across invoice number\n- Filter by project, type, status, dates, and amount\n- Sort by any field\n\nRetrieves a paginated grid of canceled invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, statusChangedAt, netto, status. Sortable fields: id, number, projectId, type, createdAt, statusChangedAt, netto, status.","operationId":"InvoicesController_getCanceledInvoices","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: number","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Invoice ID\n- **number** (string): Invoice number\n- **projectId** (string): Project ID\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt** (date): Invoice creation date\n- **statusChangedAt** (date): Date when invoice was cancelled\n- **netto** (number): Net amount (before taxes and fees)\n- **status** (set): Invoice status (Cancelled)\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **statusChangedAt** (date): Date when invoice was cancelled (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **status** (set): Invoice status (Cancelled) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Invoice ID\n- **number**: Invoice number\n- **projectId**: Project ID\n- **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt**: Invoice creation date\n- **statusChangedAt**: Date when invoice was cancelled\n- **netto**: Net amount (before taxes and fees)\n- **status**: Invoice status (Cancelled)\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** statusChangedAt (desc)","schema":{"example":"[{\"field\":\"statusChangedAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, number, projectId, type, createdAt, statusChangedAt, netto, status","schema":{"example":"id,number","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved canceled invoices grid"},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get canceled invoices grid","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/invoices/overdue":{"get":{"description":"Returns a paginated, filterable, and sortable grid of invoices that are past their payment due date.\n\n**What are Overdue Invoices?**\nThe \"Overdue\" tab shows all invoices where the payment term has passed. These invoices are in status \"New\" or \"Sent\" and have a due date that is in the past. This view helps you monitor open receivables, send reminders, and manage payment deadlines.\n\n**What is returned:**\n- Invoice identification (ID, number)\n- Project reference (projectId)\n- Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- Creation and sending dates (createdAt, sentAt)\n- Payment due date (dueDate)\n- Reminder dates (firstReminderAt, secondReminderAt)\n- Number of days overdue (overdueDays)\n- Net amount before taxes and fees (netto)\n- Invoice status (New, Sent)\n\n**Filtering and search:**\n- Quick search across invoice number\n- Filter by project, type, status, dates, amount, and overdue days\n- Sort by any field (default: overdueDays descending)\n\nRetrieves a paginated grid of overdue invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, sentAt, dueDate, firstReminderAt, secondReminderAt, netto, status, overdueDays. Sortable fields: id, number, projectId, type, createdAt, sentAt, dueDate, firstReminderAt, secondReminderAt, netto, status, overdueDays.","operationId":"InvoicesController_getOverdueInvoices","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: number","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Invoice ID\n- **number** (string): Invoice number\n- **projectId** (string): Project ID\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt** (date): Invoice creation date\n- **sentAt** (date): Date when invoice was sent\n- **dueDate** (date): Payment due date\n- **firstReminderAt** (date): Date when first reminder was sent\n- **secondReminderAt** (date): Date when second reminder was sent\n- **netto** (number): Net amount (before taxes and fees)\n- **status** (set): Invoice status (New, Sent)\n- **overdueDays** (number): Number of days overdue\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **sentAt** (date): Date when invoice was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **dueDate** (date): Payment due date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **firstReminderAt** (date): Date when first reminder was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **secondReminderAt** (date): Date when second reminder was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **status** (set): Invoice status (New, Sent) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **overdueDays** (number): Number of days overdue (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Invoice ID\n- **number**: Invoice number\n- **projectId**: Project ID\n- **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)\n- **createdAt**: Invoice creation date\n- **sentAt**: Date when invoice was sent\n- **dueDate**: Payment due date\n- **firstReminderAt**: Date when first reminder was sent\n- **secondReminderAt**: Date when second reminder was sent\n- **netto**: Net amount (before taxes and fees)\n- **status**: Invoice status (New, Sent)\n- **overdueDays**: Number of days overdue\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** overdueDays (desc)","schema":{"example":"[{\"field\":\"overdueDays\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, number, projectId, type, createdAt, sentAt, dueDate, firstReminderAt, secondReminderAt, netto, status, overdueDays","schema":{"example":"id,number","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved overdue invoices grid"},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get overdue invoices grid","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/invoices":{"post":{"description":"Creates a new invoice from a potential invoice. A potential invoice represents billable items (tasks, products, subscriptions, express quotations) that are ready to be invoiced.\n\n**How invoice creation works:**\n1. First, retrieve available potential invoices using GET /billing/potential-invoices\n2. Review and customize the potential invoice details if needed (adjust billing periods, skip tasks, add manual positions)\n3. Create the invoice using this endpoint with the potential invoice ID\n4. The system automatically:\n   - Generates a sequential invoice number in the format: `{ORG}{PRJ}-{YY}{MM}-{NNN}`\n   - Calculates all amounts, taxes, and fees\n   - Marks tasks, subscriptions, and express quotations as billed\n   - Updates project billing status\n   - Handles interim payments\n\n**What happens after creation:**\n- The invoice is created in status \"New\" (not yet sent)\n- All billable items included in the invoice are marked as billed\n- The invoice appears in the \"New\" invoices grid\n- You can then send the invoice using the invoice sending endpoints\n\n**Potential Invoice ID Format:**\nThe `potentialInvoiceId` follows the format: `{type}::{projectId}::{dateFrom}::{dateTo}`\n- Type: One of Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment\n- ProjectId: UUID of the project\n- DateFrom/DateTo: Dates in YYYY-MM-DD format\nExample: `SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31`\n\n**What is returned:**\n- Invoice ID (UUID) - Use this to reference the invoice in other endpoints\n- Invoice number - The official sequential invoice number for accounting","operationId":"InvoicesController_createInvoice","parameters":[],"requestBody":{"required":true,"description":"Potential invoice identifier to create invoice from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvoiceDto"}}}},"responses":{"200":{"description":"Invoice created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponseDto"}}}},"400":{"description":"Invalid potential invoice ID or validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/invoiceContacts/{projectId}":{"get":{"description":"Returns a list of organization members (contacts) from the project's customer organization who can be selected as invoice recipients. These contacts are the people who will receive invoices when they are sent via email.\n\n**What are Invoice Contacts?**\nInvoice contacts are organization members from the customer organization associated with the project. These are the people who can receive invoices via email. Each contact must have a valid email address to receive invoices.\n\n**What data is returned:**\n- List of organization members from the project's customer organization\n- Each contact includes ID (UUID), full name, avatar file ID (if available), and email address\n- Contacts are sorted alphabetically by first name, then last name\n- Only active (non-deleted) organization members are returned\n\n**How to use:**\n1. Get the list of available contacts for a project\n2. Select a contact ID from the list\n3. Use the POST /billing/setInvoiceContactId endpoint to set the contact for an invoice\n4. When the invoice is sent, it will be emailed to this contact\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project to view contacts.","operationId":"InvoicesController_getInvoiceContacts","parameters":[{"name":"projectId","required":true,"in":"path","description":"Project ID (UUID) to get contacts for","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Invoice contacts retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceContactResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Project not found or access denied"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get invoice contacts for a project","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/invoices/{id}":{"get":{"description":"Returns complete details of an existing invoice, including all billable items, financial totals, and payment tracking information.\n\n**What is returned:**\n- Invoice identification (ID, number, type, status)\n- Project reference and billing period (billedFrom, billedTo)\n- All billable items:\n  - Tasks with time tracking and hourly rates\n  - Products with options, quantities, and pricing\n  - Components with nested items and time estimates\n  - Subscriptions (fixed, variable, one-time payments)\n  - Express quotations with items and options\n  - Support contingent charges (retainer plan charges for Support-type projects)\n  - Support contingent usage (compensation/deductions from support contingents, negative amounts)\n  - Manual positions (custom billing items)\n- Financial totals:\n  - Net amount (before taxes and fees)\n  - VAT amount and percentage\n  - Leadtime fee (if applicable)\n  - Gross amount (after taxes)\n  - Full total (including all fees)\n- Payment tracking:\n  - Due date and overdue status\n  - Sent dates and recipients\n  - Reminder dates (first and second)\n  - Payment received date\n  - Cancellation information\n- Overdue fees and interest calculations\n\n**Description fields:**\nAll description fields (products, components, manual positions, express quotations) are returned as HTML format for easy display in web applications.\n\n**Use cases:**\n- View complete invoice breakdown for accounting\n- Display invoice details in customer portals\n- Generate custom invoice documents\n- Track payment status and overdue amounts\n- Audit invoice contents and calculations","operationId":"InvoicesController_getInvoiceDetails","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Successfully retrieved invoice details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceDetailsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get invoice details","tags":["billing","invoices"],"x-required-scopes":["api:read"]},"delete":{"description":"Deletes a new invoice that has not yet been sent. When an invoice is deleted, all included billable items are returned to potential invoices and can be billed again.\n\n**What happens when you delete an invoice:**\n1. The invoice is soft-deleted (marked with deletedAt timestamp)\n2. All included billable items are reverted to unbilled status:\n   - Tasks: marked as unbilled (`billed=false`), invoice time cleared\n   - Express quotations: marked as unbilled (`billed=false`)\n   - Subscription billing rows: marked as unbilled (`billed=false`)\n   - Projects: marked as unbilled (`billed=false`) for SingleProject invoices\n   - Interim payments: marked as unbilled (`isBilled=false`)\n3. All items become available again in potential invoices (GET /billing/potential-invoices)\n\n**Important restrictions:**\n- Only invoices with status \"New\" can be deleted\n- Invoices that have been sent (status \"Sent\", \"Paid\", or \"Cancelled\") cannot be deleted\n- To cancel a sent invoice, use the cancel invoice endpoint instead\n\n**Use cases:**\n- Remove an invoice that was created by mistake\n- Return items to potential invoices for re-billing with different settings\n- Correct billing errors before sending the invoice\n\n**What is returned:**\n- Success response confirming the invoice was deleted\n- All billable items are automatically returned to potential invoices","operationId":"InvoicesController_deleteInvoice","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Invoice deleted successfully. All billable items have been returned to potential invoices.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot be deleted (only invoices with status \"New\" can be deleted)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete an invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/mark-as-paid":{"post":{"description":"Marks an invoice as paid, indicating that payment has been received from the customer. This changes the invoice status from \"New\" or \"Sent\" to \"Paid\" and records the payment date and runtime.\n\n**What happens when you mark an invoice as paid:**\n1. Invoice status changes to \"Paid\"\n2. Payment date (`paidAt`) is recorded as the current date\n3. Payment runtime (`paidRuntime`) is calculated as the number of days between invoice creation and payment\n4. Status change timestamp and user are recorded\n5. The invoice moves from the \"New\" or \"Overdue\" tab to the \"Paid\" tab\n\n**Important restrictions:**\n- Only invoices with status \"New\" or \"Sent\" can be marked as paid\n- Invoices that are already \"Paid\" or \"Cancelled\" cannot be marked as paid\n- To revert a paid invoice, use the mark as unpaid endpoint\n\n**Use cases:**\n- Record payment received from customer\n- Update invoice status after manual payment confirmation\n- Track payment timing for financial reporting\n\n**What is returned:**\n- Success response confirming the invoice was marked as paid","operationId":"InvoicesController_markAsPaid","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Invoice marked as paid successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot be marked as paid (only invoices with status \"New\" or \"Sent\" can be marked as paid)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark invoice as paid","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/mark-as-unpaid":{"post":{"description":"Reverts a paid invoice back to unpaid status. This changes the invoice status from \"Paid\" back to \"New\" or \"Sent\" (depending on whether it was sent before), allowing you to correct payment records.\n\n**What happens when you mark an invoice as unpaid:**\n1. Invoice status changes from \"Paid\" back to \"New\" or \"Sent\"\n   - If the invoice was sent before, it returns to \"Sent\" status\n   - If the invoice was never sent, it returns to \"New\" status\n2. Payment date and runtime are cleared\n3. Status change timestamp and user are recorded\n4. The invoice moves from the \"Paid\" tab back to the appropriate tab\n\n**Important restrictions:**\n- Only invoices with status \"Paid\" can be marked as unpaid\n- Invoices that are \"Cancelled\" cannot be marked as unpaid\n- This operation is useful for correcting payment recording errors\n\n**Use cases:**\n- Correct payment recording errors\n- Revert accidental payment marking\n- Handle payment reversals or refunds\n\n**What is returned:**\n- Success response confirming the invoice was marked as unpaid","operationId":"InvoicesController_markAsUnpaid","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Invoice marked as unpaid successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot be marked as unpaid (only invoices with status \"Paid\" can be marked as unpaid)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark invoice as unpaid","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/mark-as-sent":{"post":{"description":"Marks an invoice as sent to the customer. This changes the invoice status from \"New\" to \"Sent\", records the sending date, and calculates the payment due date based on organization or workspace payment terms.\n\n**What happens when you mark an invoice as sent:**\n1. Invoice status changes from \"New\" to \"Sent\"\n2. Sending date (`sentAt`) is recorded as the current date\n3. Sending type is set to \"Manual\" (indicating manual marking, not email sending)\n4. Contact recipient is recorded (from invoice contactId)\n5. Payment due date (`dueDate`) is calculated based on:\n   - Organization-specific payment terms (`invoiceDueDays`), or\n   - Workspace default payment terms (`defaultInvoiceDueDays`)\n6. Status change timestamp and user are recorded\n7. The payment term starts running from this point\n\n**Important restrictions:**\n- Only invoices with status \"New\" or \"Sent\" can be marked as sent\n- Invoices that are \"Paid\" or \"Cancelled\" cannot be marked as sent\n- This endpoint marks the invoice as sent manually (use send invoice endpoint for email sending)\n\n**Use cases:**\n- Record that an invoice was sent manually (e.g., via postal mail or other channels)\n- Update invoice status after external sending\n- Start payment term tracking\n\n**What is returned:**\n- Success response confirming the invoice was marked as sent","operationId":"InvoicesController_markAsSent","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Invoice marked as sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot be marked as sent (only invoices with status \"New\" or \"Sent\" can be marked as sent)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark invoice as sent","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/mark-first-reminder-as-sent":{"post":{"description":"Marks the first reminder for an invoice as sent to the customer. This records the first reminder date and contact information.\n\n**What happens when you mark a first reminder as sent:**\n1. First reminder date (`firstReminderAt`) is recorded as the current date\n2. First reminder type is set to \"Manual\" (indicating manual marking, not email sending)\n3. Contact recipient is recorded (from invoice contactId)\n4. User who marked it as sent is recorded\n\n**Important restrictions:**\n- Only invoices with status \"New\" or \"Sent\" can have reminders marked as sent\n- The invoice must be overdue (past its due date) to send reminders\n- This endpoint marks the reminder as sent manually (use send reminder endpoint for email sending)\n\n**Use cases:**\n- Record that a first reminder was sent manually (e.g., via postal mail or other channels)\n- Update reminder status after external sending\n- Track reminder history\n\n**What is returned:**\n- Success response confirming the first reminder was marked as sent","operationId":"InvoicesController_markFirstReminderAsSent","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"First reminder marked as sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot have reminder marked as sent"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark first reminder as sent","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/mark-second-reminder-as-sent":{"post":{"description":"Marks the second reminder for an invoice as sent to the customer. This records the second reminder date and contact information.\n\n**What happens when you mark a second reminder as sent:**\n1. Second reminder date (`secondReminderAt`) is recorded as the current date\n2. Second reminder type is set to \"Manual\" (indicating manual marking, not email sending)\n3. Contact recipient is recorded (from invoice contactId)\n4. User who marked it as sent is recorded\n\n**Important restrictions:**\n- Only invoices with status \"New\" or \"Sent\" can have reminders marked as sent\n- The invoice must be overdue (past its due date) to send reminders\n- A first reminder should typically be sent before a second reminder\n- This endpoint marks the reminder as sent manually (use send reminder endpoint for email sending)\n\n**Use cases:**\n- Record that a second reminder was sent manually (e.g., via postal mail or other channels)\n- Update reminder status after external sending\n- Track reminder history\n\n**What is returned:**\n- Success response confirming the second reminder was marked as sent","operationId":"InvoicesController_markSecondReminderAsSent","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Second reminder marked as sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot have reminder marked as sent"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Mark second reminder as sent","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/setInvoiceContactId":{"post":{"description":"Updates the contact ID for an existing invoice. The contact must be a valid organization member from the invoice's project customer organization.\n\n**What happens when you set an invoice contact:**\n1. The invoice's contactId field is updated\n2. The contact must belong to the project's customer organization\n3. If contactId is set to null, the contact is cleared\n4. The contact will receive the invoice when it is sent via email\n\n**Important restrictions:**\n- The contact must be a valid organization member from the project's customer organization\n- Use GET /billing/invoiceContacts/{projectId} to get available contacts\n- The contact must have a valid email address to receive invoices\n\n**Use cases:**\n- Set or change the invoice recipient before sending\n- Clear the contact if needed\n- Update contact after invoice creation\n\n**What is returned:**\n- Success response confirming the contact was set","operationId":"InvoicesController_setInvoiceContactId","parameters":[],"requestBody":{"required":true,"description":"Invoice ID and contact ID to set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetInvoiceContactIdDto"}}}},"responses":{"200":{"description":"Invoice contact set successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invalid invoice ID or contact ID"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found or contact not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Set invoice contact ID","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/sendInvoice":{"post":{"description":"Sends an invoice to the customer via email as a PDF attachment. The invoice must have a contact with a valid email address set.\n\n**What happens when you send an invoice:**\n1. Invoice PDF document is generated\n2. Invoice status changes from \"New\" to \"Sent\"\n3. Sending date (`sentAt`) is recorded as the current date\n4. Sending type is set to \"Email\" (indicating email sending)\n5. Contact recipient is recorded (from invoice contactId)\n6. Payment due date (`dueDate`) is calculated based on:\n   - Organization-specific payment terms (`invoiceDueDays`), or\n   - Workspace default payment terms (`defaultInvoiceDueDays`)\n7. Status change timestamp and user are recorded\n8. Email is sent to the contact's email address with PDF attachment\n9. The payment term starts running from this point\n\n**Important restrictions:**\n- Only invoices with status \"New\" or \"Sent\" can be sent\n- Invoices that are \"Paid\" or \"Cancelled\" cannot be sent\n- The invoice must have a contact with a valid email address\n- Use POST /billing/setInvoiceContactId to set the contact if needed\n\n**Use cases:**\n- Send invoice to customer via email\n- Automate invoice delivery\n- Track invoice sending and payment terms\n\n**What is returned:**\n- Success response confirming the invoice was sent","operationId":"InvoicesController_sendInvoice","parameters":[],"requestBody":{"required":true,"description":"Invoice ID to send","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInvoiceDto"}}}},"responses":{"200":{"description":"Invoice sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Invoice cannot be sent (only invoices with status \"New\" or \"Sent\" can be sent, or contact email is missing)"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Send invoice via email","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/settings":{"get":{"description":"Returns invoice-specific settings including language, reminder fees, interest rates, and custom text templates. Invoice settings can override organization or workspace defaults.\n\n**What is returned:**\n- Invoice-specific settings (language, reminder fees, interest rates)\n- Parent settings (from organization or workspace) for reference\n- Custom text templates (returned as HTML) for:\n  - Closing salutation (withBestRegards)\n  - Invoice greeting\n  - Invoice footer\n  - Reminder fee warning\n  - First and second reminder greetings and footers\n  - Cancellation body\n\n**Settings hierarchy:**\nInvoice settings override organization settings, which override workspace defaults. If a setting is null at the invoice level, the parent setting is used.","operationId":"InvoicesController_getInvoiceSettings","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Successfully retrieved invoice settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get invoice settings","tags":["billing","invoices"],"x-required-scopes":["api:read"]},"patch":{"description":"Partially updates invoice-specific settings including language, reminder fees, interest rates, and custom text templates. Only provided fields are updated; omitted fields remain unchanged.\n\n**What can be updated:**\n- Invoice language\n- Reminder fee settings (enable/disable, amount)\n- Interest settings (enable/disable, base rate, rate)\n- Custom text templates (accepts HTML or Markdown)\n\n**Settings behavior:**\n- Setting a value to `null` will use the parent setting (organization or workspace default)\n- Custom texts can be provided as HTML or Markdown and will be converted to internal format\n- Empty custom texts are treated as null (uses defaults)\n\n**Custom text templates:**\nAll custom text fields support invoice variables (e.g., `#invoiceNumber`, `#projectName`, `#clientCompanyName`). See the invoice text variables documentation for a complete list of available variables.\n\n**What is returned:**\nUpdated invoice settings with custom texts returned as HTML.","operationId":"InvoicesController_updateInvoiceSettings","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"description":"Invoice settings to update (all fields optional)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchInvoiceSettingsDto"}}}},"responses":{"200":{"description":"Invoice settings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceSettingsResponseDto"}}}},"400":{"description":"Invalid request data or validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update invoice settings","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/invoices/{id}/default-texts":{"get":{"description":"Returns default invoice text templates for a specific language. These are the default texts that will be used if no custom texts are set for the invoice.\n\n**How default texts are determined:**\n1. Workspace-level defaults for the specified language\n2. Organization-level defaults (if available)\n3. System defaults (translated to the specified language)\n\n**Language selection:**\nIf no language is provided, the system uses:\n1. Invoice language (if set)\n2. Organization invoice language (if set)\n3. Workspace invoice language (if set)\n4. Workspace default language\n\n**What is returned:**\nAll default text templates as HTML:\n- Closing salutation (withBestRegards)\n- Invoice greeting\n- Invoice footer\n- Reminder fee warning\n- First and second reminder greetings and footers\n- Cancellation body\n\n**Use cases:**\n- Preview default texts before customizing\n- Reset custom texts to defaults\n- Display default texts in UI for reference","operationId":"InvoicesController_getDefaultInvoiceTexts","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"format":"uuid","type":"string"}},{"name":"language","required":false,"in":"query","description":"Language code for default texts (e.g., \"en\", \"de\"). If not provided, uses invoice/organization/workspace language.","schema":{"example":"en","type":"string"}}],"responses":{"200":{"description":"Successfully retrieved default invoice texts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefaultInvoiceTextsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get default invoice texts","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/invoices/{id}/file-url":{"get":{"description":"Returns a public URL to access the generated invoice file. The file is always regenerated to ensure it reflects the latest invoice settings and data. Files are stored with a hash-based key (invoice ID + file type + document type) to avoid creating duplicate files for the same invoice variant.\n\n**What is returned:**\n- Public URL that can be used to download or access the invoice file\n- Filename of the generated file\n\n**File types supported:**\n- `pdf`: PDF with embedded ZUGFeRD/XRechnung XML (hybrid PDF)\n- `pdf-without-invoice`: PDF without embedded XML\n- `xml-einvoice`: XML E-Invoice file (XRechnung format)\n\n**Document types supported:**\n- `invoice`: Standard invoice document\n- `cancel`: Cancellation/credit note document\n- `first-reminder`: First payment reminder\n- `second-reminder`: Second payment reminder\n\n**File storage behavior:**\nFiles are stored with a hash-based key (invoice ID + file type + document type) to ensure the same file URL is returned for the same invoice variant. However, files are always regenerated to ensure they reflect the latest invoice settings and data. This ensures accuracy while maintaining consistent URLs.\n\n**Use cases:**\n- Generate invoice PDFs for customer portals\n- Download invoices for accounting purposes\n- Access invoice files programmatically\n- Share invoice URLs with customers or partners","operationId":"InvoicesController_getInvoiceFileUrl","parameters":[{"name":"id","required":true,"in":"path","description":"Invoice ID (UUID)","schema":{"example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}},{"name":"fileType","required":true,"in":"query","description":"Type of file to generate","schema":{"example":"pdf","type":"string","enum":["pdf","pdf-without-invoice","xml-einvoice"]}},{"name":"documentType","required":true,"in":"query","description":"Type of document to generate","schema":{"example":"invoice","type":"string","enum":["invoice","cancel","first-reminder","second-reminder"]}}],"responses":{"200":{"description":"Invoice file URL retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceFileUrlResponseDto"}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Invoice not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get invoice file URL","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/potential-invoices":{"get":{"description":"**What are Potential Invoices?**\nPotential invoices represent billable items that are ready to be invoiced but have not been converted to actual invoices yet. These are invoice drafts that appear in the invoice review section, waiting to be checked, adjusted, and finalized into official invoice documents.\n\n**What gets included:**\n- Subscription billing rows that have passed their billing date\n- Time-based work (tasks with logged time) that has not been billed yet\n- Express quotations that have been accepted by customers\n- Products and components that are ready for billing\n- Manual positions and interim payments\n\n**What data is returned:**\n- List of all potential invoices grouped by project, organization, and billing period\n- Each potential invoice includes the billing period (dateFrom, dateTo), total net amount, and invoice type\n- Results are automatically filtered based on project access permissions\n- Each entry shows which organization and project it belongs to\n\n**Invoice Types:**\n- **Mixed**: Combined invoice with multiple billing types (time, subscriptions, products, components)\n- **Subscription**: Recurring subscription billing only (monthly or periodic fees)\n- **TimeBased**: Time-based work (logged hours on tasks) only\n- **ExpressQuotation**: Accepted express quotations only (quick add-ons from tickets)\n- **SingleProject**: Single project invoice (project-based billing)\n- **InterimPayment**: Interim or advance payment invoice\n\n**How it works:**\nThe system automatically creates potential invoices when billable items become due. For example, completed tickets appear as time-based items, subscriptions generate entries on their billing dates, and accepted express quotations become billable immediately.\n\n**Permission requirements:**\nRequires Invoices.manage permission to view potential invoices. Results are scoped to projects you have access to.","operationId":"PotentialInvoicesController_getPotentialInvoices","parameters":[],"responses":{"200":{"description":"Potential invoices retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PotentialInvoiceResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get potential invoices","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/potential-invoices/{id}/contacts":{"get":{"description":"**What are Invoice Contacts?**\nReturns a list of organization members (contacts) from the customer organization who can be selected as invoice recipients. These contacts are the people who will receive the invoice when it is sent via email. You can set a contact for each potential invoice to specify who should receive the final invoice document.\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\nThe projectId is extracted from the invoice ID to determine which organization is the customer, and then returns contacts from that organization.\n\n**What data is returned:**\n- List of organization members from the project customer organization\n- Each contact includes ID (UUID), full name, avatar file ID (if available), and email address\n- Contacts are sorted alphabetically by first name, then last name\n- Only active (non-deleted) organization members are returned\n\n**How to use:**\n1. Get the list of available contacts for a potential invoice\n2. Select a contact ID from the list\n3. Use the PATCH endpoint to set the contactId for the potential invoice\n4. When the invoice is finalized and sent, it will be emailed to this contact\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project to view contacts.","operationId":"PotentialInvoicesController_getInvoiceContacts","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"responses":{"200":{"description":"Invoice contacts retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceContactResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get available contacts for potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:read"]}},"/billing/potential-invoices/{id}":{"get":{"description":"**What are Potential Invoice Details?**\nReturns the complete breakdown of a specific potential invoice, showing all billable items that make up the total amount. This is the detailed view you see when reviewing an invoice before finalizing it. You can see exactly what is being billed, adjust quantities or amounts, and configure additional settings.\n\n**What is included in the details:**\n- **Subscription billing rows**: Recurring fees with quantities, unit prices, and payment types (fixed, per-unit, one-time)\n- **Time-based tasks**: Completed tickets with logged hours, estimated time, billed time, and hourly rates\n- **Express quotations**: Accepted quick quotes from tickets with items, quantities, and unit prices\n- **Products**: Items from the product catalog with options, quantities, discounts, and pricing\n- **Components**: Project components with time estimates, hourly rates, and nested items\n- **Support contingent charges**: Retainer plan charges for Support-type projects, based on allocated hours and pricing for billing periods\n- **Manual positions**: Custom billing items (third-party costs, flat fees, interim payments)\n- **Leadtime fee settings**: Configuration for adding the Leadtime service fee to the invoice\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n- Type: One of Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment, SupportContingentCharge\n- ProjectId: UUID of the project\n- DateFrom/DateTo: Dates in YYYY-MM-DD format representing the billing period\n\n**What data is returned:**\n- Complete invoice structure with all billable items organized by type\n- Billing period (periodFrom, periodTo) and custom billed dates (billedFrom, billedTo)\n- Tax calculations (tax amount, reverse charge settings)\n- Contact information (contactId for invoice recipient)\n- Leadtime fee settings (percentage, method, task distribution, custom title)\n- Lists of skipped tasks and express quotations (items excluded from billing)\n- All items include detailed pricing, discounts, descriptions (as HTML), and metadata\n- Product and component descriptions are converted from ProseMirror IDoc format to HTML\n\n**How to use:**\n1. Get the list of potential invoices to find the invoice ID\n2. Use this endpoint to get full details of a specific invoice\n3. Review all billable items and their amounts\n4. Use the PATCH endpoint to adjust settings (billed dates, contact, etc.)\n5. Use other endpoints to skip items, add manual positions, or configure fees\n6. Once reviewed and adjusted, the invoice can be finalized into an actual invoice document\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project to view potential invoice details.","operationId":"PotentialInvoicesController_getPotentialInvoiceDetails","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"responses":{"200":{"description":"Potential invoice details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PotentialInvoiceDetailsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get potential invoice details","tags":["billing","invoices"],"x-required-scopes":["api:read"]},"patch":{"description":"**What can be updated:**\nThis endpoint allows partial updates to potential invoice settings. Only the fields you provide in the request body will be updated; all other fields remain unchanged. This means you can update just one field, multiple fields, or all fields in a single request.\n\n**Updatable fields:**\n- **billedFrom**: Billed from date (ISO 8601 date string). Sets the start date for the billing period that will appear on the final invoice document. This can differ from the actual service period (periodFrom) if you want to show a different date range on the invoice.\n- **billedTo**: Billed to date (ISO 8601 date string). Sets the end date for the billing period that will appear on the final invoice document. This can differ from the actual service period (periodTo) if you want to show a different date range on the invoice.\n- **contactId**: Contact ID (organization member UUID). Sets the invoice recipient who will receive the invoice when it is sent via email. Set to null to clear the contact and remove the recipient assignment.\n\n**How partial updates work:**\n- If you only provide billedFrom, only that field will be updated\n- If you only provide contactId, only that field will be updated\n- You can provide any combination of the three fields\n- Fields you omit will remain unchanged\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Date format:**\nDates can be provided in ISO 8601 format:\n- Date-only: \"2025-10-01\" (recommended)\n- ISO timestamp: \"2025-10-01T00:00:00.000Z\"\n\n**Contact validation:**\nWhen setting a contactId, the system validates that:\n- The contact exists and is active\n- The contact belongs to the project customer organization\n- If the contact is invalid, a NotFoundException is returned\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project to update potential invoice settings.","operationId":"PotentialInvoicesController_patchPotentialInvoice","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchPotentialInvoiceDto"}}}},"responses":{"200":{"description":"Potential invoice updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update potential invoice settings","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/reset":{"post":{"description":"**What does reset do?**\nResets all customizations and metadata for a potential invoice, returning it to its default state. This clears all manual adjustments you have made, so the invoice will show the original billable items as they were automatically calculated by the system.\n\n**What gets cleared:**\n- Billed from and billed to dates (reverts to default billing period)\n- Contact ID (removes invoice recipient)\n- Subscription quantities and order (reverts to default quantities)\n- Custom manual positions (removes any manually added items)\n- Skipped tasks and express quotations (includes all items back in the invoice)\n- Leadtime fee settings (reverts to default configuration)\n\n**When to use:**\n- When you want to start fresh with default invoice settings\n- After making mistakes in invoice configuration and want to undo all changes\n- To clear all customizations before creating a new invoice from scratch\n- When you need to see the original calculated amounts without any adjustments\n\n**Important notes:**\n- This operation cannot be undone\n- The invoice will revert to showing all automatically calculated billable items\n- Any custom manual positions you added will be removed\n- The invoice will use default billing period dates\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project to reset potential invoice.","operationId":"PotentialInvoicesController_resetPotentialInvoice","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"responses":{"200":{"description":"Potential invoice reset successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reset potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/tasks/{taskId}/toggle-skipped":{"post":{"description":"**What does this do?**\nToggles whether a task is skipped (excluded) from the potential invoice. When a task is skipped, it is added to the skipped tasks list and will not appear on the final invoice. Toggling again removes it from the skipped list and includes it back in the invoice.\n\n**When to use:**\n- To exclude a task from billing without permanently marking it as \"do not bill\"\n- To temporarily remove a task from an invoice during review\n- To include a previously skipped task back in the invoice\n\n**How it works:**\n- First call: Adds task to skipped tasks list (excludes from invoice)\n- Second call: Removes task from skipped tasks list (includes in invoice)\n- The task itself is not modified, only the invoice metadata is updated\n\n**Task requirements:**\n- Task must belong to the potential invoice's project\n- Task must not be already billed (billed: false)\n- User must have access to the project\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_toggleSkippedTask","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"taskId","required":true,"in":"path","description":"Task ID (UUID) to toggle skipped status for","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Task skipped status toggled successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Toggle task skipped status","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/tasks/{taskId}/billed-time":{"post":{"description":"**What does this do?**\nSets the billed time (hours) for a task in a potential invoice. The billed time is the amount of time that will be charged to the customer, which may differ from the actual time spent on the task.\n\n**When to use:**\n- To adjust the billed hours up or down from the actual time spent\n- To bill the estimated time instead of actual time\n- To correct billing amounts before finalizing the invoice\n\n**How it works:**\n- Updates the task's billedTime field directly\n- The billed time is used to calculate the total charge (billedTime × hourly rate)\n- Can be set to any non-negative number\n- Setting a different value than spent time will show a difference in the invoice details\n\n**Task requirements:**\n- Task must belong to the potential invoice's project\n- Task must not be already billed (billed: false)\n- User must have access to the project\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_setTaskBilledTime","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"taskId","required":true,"in":"path","description":"Task ID (UUID) to set billed time for","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTaskBilledTimeDto"}}}},"responses":{"200":{"description":"Task billed time set successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Set task billed time","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/tasks/{taskId}/details":{"post":{"description":"**What does this do?**\nSets both the billed time and billed title for a task in a potential invoice. This allows you to customize how the task appears on the invoice, including adjusting the time charged and providing a more customer-friendly title.\n\n**When to use:**\n- To adjust both the billed hours and the task title in one operation\n- To customize how tasks appear on invoices for better customer clarity\n- To correct billing details before finalizing the invoice\n\n**How it works:**\n- Updates both task.billedTime and task.billedTitle fields\n- Both fields are optional - you can update just one or both\n- billedTime can be set to null to clear it (revert to using spent time)\n- billedTitle can be set to null to use the original task title\n- The billed time is used to calculate the total charge (billedTime × hourly rate)\n\n**Task requirements:**\n- Task must belong to the potential invoice's project\n- Task must not be already billed (billed: false)\n- User must have access to the project\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_setTaskDetails","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"taskId","required":true,"in":"path","description":"Task ID (UUID) to update details for","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTaskDetailsDto"}}}},"responses":{"200":{"description":"Task details updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Set task billing details","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/tasks/{taskId}/skip-from-billing":{"post":{"description":"**What does this do?**\nSets the skipFromBilling flag on a task, which permanently marks the task as excluded from billing. Unlike toggling skipped status (which only affects the current invoice), this flag prevents the task from appearing in any future potential invoices.\n\n**When to use:**\n- To permanently exclude a task from billing (e.g., internal work, non-billable tasks)\n- To mark tasks that should never be invoiced\n- To undo a previous \"skip from billing\" decision (set to false)\n\n**How it works:**\n- Updates the task.skipFromBilling field directly on the task\n- When set to true, the task will not appear in any potential invoices\n- When set to false, the task can be included in invoices again\n- This is different from toggling skipped status, which only affects the current invoice metadata\n\n**Task requirements:**\n- Task must belong to the potential invoice's project\n- Task must not be already billed (billed: false)\n- User must have access to the project\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_skipFromBilling","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"taskId","required":true,"in":"path","description":"Task ID (UUID) to set skip from billing flag for","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkipFromBillingDto"}}}},"responses":{"200":{"description":"Task skip from billing flag set successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or task not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Set task skip from billing flag","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/manual-positions":{"post":{"description":"**What does this do?**\nAdds a custom manual position (line item) to a potential invoice. Manual positions allow you to bill for external costs, flat fees, or other items that aren't automatically created by tickets, products, or subscriptions.\n\n**When to use:**\n- To add external costs (e.g., translation services, license costs)\n- To add travel expenses or other one-time charges\n- To add flat rates with no ticket reference\n- To add items that were missing from the original offer\n\n**How it works:**\n- Creates a new manual position entry stored in the potential invoice metadata\n- The position appears immediately in the invoice preview\n- Manual positions are fully integrated into tax and total calculations\n- Only custom manual positions (type: manual) can be added, updated, or deleted\n- Project manual positions (from project snapshots) are read-only and cannot be modified\n\n**Position requirements:**\n- Must provide a unique UUID for the position ID\n- Title and price are required\n- Price must be a positive number (net amount before tax)\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_addManualPosition","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddManualPositionDto"}}}},"responses":{"200":{"description":"Manual position added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Add manual position to potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/manual-positions/{positionId}":{"patch":{"description":"**What does this do?**\nUpdates an existing custom manual position in a potential invoice. You can modify the title and/or price of the position.\n\n**When to use:**\n- To correct the title or price of a manual position\n- To adjust pricing before finalizing the invoice\n- To update position details after initial creation\n\n**How it works:**\n- Updates the manual position stored in the potential invoice metadata\n- Only custom manual positions (type: manual) can be updated\n- Project manual positions (from project snapshots) are read-only and cannot be modified\n- Both title and price must be provided (even if only one is changing)\n\n**Position requirements:**\n- Position must exist in the potential invoice\n- Position must be a custom manual position (not a project position)\n- Title and price are required\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_updateManualPosition","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"positionId","required":true,"in":"path","description":"Manual position ID (UUID) to update","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateManualPositionDto"}}}},"responses":{"200":{"description":"Manual position updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or manual position not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update manual position in potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]},"delete":{"description":"**What does this do?**\nRemoves a custom manual position from a potential invoice. The position is permanently removed from the invoice and will no longer appear in the invoice preview or be included in calculations.\n\n**When to use:**\n- To remove a manual position that was added by mistake\n- To remove a position that is no longer needed\n- To clean up the invoice before finalizing\n\n**How it works:**\n- Removes the manual position from the potential invoice metadata\n- Only custom manual positions (type: manual) can be deleted\n- Project manual positions (from project snapshots) are read-only and cannot be deleted\n- The position is immediately removed from the invoice preview\n\n**Position requirements:**\n- Position must exist in the potential invoice\n- Position must be a custom manual position (not a project position)\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_deleteManualPosition","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"positionId","required":true,"in":"path","description":"Manual position ID (UUID) to delete","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Manual position deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or manual position not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete manual position from potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/manual-positions/sort":{"post":{"description":"**What does this do?**\nReorders the manual positions in a potential invoice. The positions will appear in the order specified by the items array.\n\n**When to use:**\n- To change the display order of manual positions on the invoice\n- To group related positions together\n- To organize positions in a logical sequence\n\n**How it works:**\n- Updates the sort order of manual positions stored in the potential invoice metadata\n- Only custom manual positions (type: manual) can be reordered\n- Project manual positions (from project snapshots) maintain their original order\n- The items array should contain all position IDs in the desired order\n- Positions not included in the array will be placed at the end\n\n**Position requirements:**\n- All position IDs in the items array must exist in the potential invoice\n- Only custom manual positions can be reordered\n- The array can include a subset of positions (others will be placed at the end)\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_saveManualPositionsSort","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveManualPositionsSortDto"}}}},"responses":{"200":{"description":"Manual positions reordered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder manual positions in potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/subscriptions/sort":{"post":{"description":"**What does this do?**\nReorders the subscription billing rows in a potential invoice. The subscriptions will appear on the invoice in the order specified by the items array.\n\n**When to use:**\n- To change the display order of subscription billing rows on the invoice\n- To group related subscriptions together\n- To organize subscriptions in a logical sequence (e.g., by priority or billing type)\n\n**How it works:**\n- Updates the sort order of subscription billing rows stored in the potential invoice metadata\n- The items array should contain all subscription billing IDs (billingId) in the desired order\n- Subscriptions not included in the array will maintain their original order\n- The order affects how subscriptions appear on the final invoice document\n\n**Subscription requirements:**\n- All subscription billing IDs in the items array must exist in the potential invoice\n- Use the billingId field from subscription billing rows (not the id field)\n- The array can include a subset of subscriptions (others will maintain their order)\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_saveSubscriptionSort","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSubscriptionSortDto"}}}},"responses":{"200":{"description":"Subscriptions reordered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder subscriptions in potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/billing/potential-invoices/{id}/subscriptions/{itemId}/quantity":{"post":{"description":"**What does this do?**\nSets a custom quantity for a subscription billing row in a potential invoice. For per-unit pricing subscriptions, this allows you to adjust how many units are billed, which may differ from the default quantity calculated by the system.\n\n**When to use:**\n- To adjust the quantity for per-unit pricing subscriptions before finalizing the invoice\n- To bill a different number of units than the default calculated amount\n- To correct billing quantities based on actual usage or customer agreements\n- To clear a custom quantity and revert to the default calculated quantity\n\n**How it works:**\n- Updates the subscription quantity stored in the potential invoice metadata\n- The quantity is used to calculate the total charge (quantity × unit price)\n- Setting value to null or omitting it clears the custom quantity and reverts to default\n- The quantity must be a non-negative integer\n- Only affects the current potential invoice; does not modify the subscription billing configuration\n\n**Subscription requirements:**\n- Subscription billing ID (itemId) must exist in the potential invoice\n- Use the billingId field from subscription billing rows\n- Only per-unit pricing subscriptions (payment type: perUnit) typically use quantities\n- Fixed monthly fees and one-time charges may not use quantities\n\n**Invoice ID Format:**\nThe invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}\n- Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31\n\n**Permission requirements:**\nRequires Invoices.manage permission and access to the project.","operationId":"PotentialInvoicesController_changeQuantity","parameters":[{"name":"id","required":true,"in":"path","description":"Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}","schema":{"example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Subscription billing ID (billingId UUID) to change quantity for","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeQuantityDto"}}}},"responses":{"200":{"description":"Subscription quantity updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"User lacks Invoices.manage permission or project access"},"404":{"description":"Potential invoice or subscription billing not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Change subscription quantity in potential invoice","tags":["billing","invoices"],"x-required-scopes":["api:write"]}},"/helpdesk/settings":{"get":{"description":"Retrieves workspace-wide helpdesk settings including default configurations and email account settings.\n\n**What is Helpdesk?**\nHelpdesk is an automated email-to-task conversion system that transforms incoming emails into tasks. When enabled, the system automatically:\n- Pulls emails from configured email accounts (IMAP, Gmail, Microsoft Graph)\n- Converts emails into tasks with proper assignment, priority, and categorization\n- Sends automatic replies to new senders (if configured)\n- Links emails to tasks for full conversation history\n\n**What is returned:**\n- **Workspace settings**: Global helpdesk configuration including:\n  - `isHelpdeskEnabled`: Whether helpdesk is enabled for the workspace\n  - `defaultSettings`: Default task creation settings (project, type, status, priority, assignment)\n  - `defaultReplyBody`: HTML template for automatic replies (supports variables like `taskNumber`)\n  - `sendDefaultReply`: Whether to automatically send replies to new senders\n- **Email account settings**: List of all email accounts with their helpdesk status:\n  - Each account shows if helpdesk is enabled\n  - Account-specific overrides that override workspace defaults\n  - Settings include task defaults, auto-reply configuration, and assignment rules\n\n**Settings inheritance:**\nEmail accounts can override workspace defaults. When an account has its own settings:\n- Non-null fields override workspace defaults\n- Null fields inherit from workspace defaults\n- `sendDefaultReply` supports 3-state inheritance: `null` (use default), `true` (enabled), `false` (disabled)\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission.","operationId":"HelpdeskController_getHelpdeskSettings","parameters":[],"responses":{"200":{"description":"Workspace helpdesk settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpdeskSettingsApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get workspace helpdesk settings","tags":["helpdesk"],"x-required-scopes":["api:read"]},"put":{"description":"Updates workspace-wide default helpdesk settings. These settings apply to all email accounts unless overridden at the account level.\n\n**What can be configured:**\n- **Default task priority**: Priority level for all helpdesk tasks (Urgent, High, Normal, Low)\n- **Default task type**: Task type ID that will be assigned to helpdesk tasks\n- **Default task status**: Task status ID for newly created helpdesk tasks\n- **Auto-assign user**: Optional user ID to automatically assign tasks to\n- **Default reply body**: HTML template for automatic replies (supports variables like `taskNumber`)\n- **Send default reply**: Whether to automatically send replies to new email senders\n\n**How it works:**\n- These settings become the base configuration for all helpdesk operations\n- Individual email accounts can override any of these defaults\n- When an email account has no specific settings, it uses these workspace defaults\n- The `isHelpdeskEnabled` flag is preserved from current settings (not updated via this endpoint)\n\n**Auto-reply system:**\nWhen `sendDefaultReply` is enabled, the system automatically sends replies to initial emails from new senders:\n- Only triggers for first-time senders (not replies to existing conversations)\n- Uses the `defaultReplyBody` template with variable substitution\n- Variables like `taskNumber` are replaced with actual values\n- Replies are sent from the same email account that received the original email\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect all email accounts that don't have account-specific overrides.","operationId":"HelpdeskController_updateHelpdeskSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateHelpdeskSettingsDto"}}}},"responses":{"200":{"description":"Workspace helpdesk settings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpdeskSettingsApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update workspace helpdesk settings","tags":["helpdesk"],"x-required-scopes":["api:write"]}},"/helpdesk/email-accounts":{"get":{"description":"Retrieves all email accounts in the workspace with their helpdesk enabled status and settings.\n\n**What is returned:**\nA list of all email accounts configured in the workspace, each containing:\n- **Account identification**: ID, email address, and display name\n- **Helpdesk status**: Whether helpdesk is enabled for this account\n- **Account settings**: If enabled, includes all account-specific helpdesk settings:\n  - Task defaults (project, type, status, priority, assignment)\n  - Auto-reply configuration\n  - Any overrides that differ from workspace defaults\n\n**Use cases:**\n- View which email accounts have helpdesk enabled\n- Check account-specific settings and overrides\n- Identify accounts that are using workspace defaults vs. custom settings\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission.","operationId":"HelpdeskController_getEmailAccounts","parameters":[],"responses":{"200":{"description":"Email accounts retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EmailAccountHelpdeskStatusResponse"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get email accounts with helpdesk status","tags":["helpdesk"],"x-required-scopes":["api:read"]}},"/helpdesk/email-accounts/{accountId}/settings":{"get":{"description":"Retrieves helpdesk settings for a specific email account, including any account-level overrides.\n\n**What is returned:**\n- **Account information**: ID, email address, display name\n- **Helpdesk status**: Whether helpdesk is enabled for this account\n- **Account settings**: If enabled, returns all account-specific settings:\n  - Task defaults that override workspace defaults\n  - Auto-reply configuration (with 3-state inheritance support)\n  - Assignment rules\n  - Any other account-level overrides\n\n**Settings inheritance:**\nIf the account has custom settings:\n- Non-null fields override workspace defaults\n- Null fields inherit from workspace defaults\n- The response shows the effective settings (merged defaults + overrides)\n\n**Error handling:**\nReturns 404 if the email account is not found or doesn't belong to the workspace.\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission.","operationId":"HelpdeskController_getEmailAccountSettings","parameters":[{"name":"accountId","required":true,"in":"path","description":"Email account ID","schema":{"example":"account-uuid-123","type":"string"}}],"responses":{"200":{"description":"Email account settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountHelpdeskStatusResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get specific email account helpdesk settings","tags":["helpdesk"],"x-required-scopes":["api:read"]},"put":{"description":"Updates helpdesk settings for a specific email account. These settings override workspace defaults. Omitted fields inherit from workspace defaults.\n\n**What can be configured:**\nAll fields are optional - only provided fields will override workspace defaults:\n- **Task priority**: Override default priority for this account\n- **Task type**: Override default task type ID\n- **Task status**: Override default task status ID\n- **Auto-assign user**: Override default assignment (set to `null` to remove override)\n- **Default reply body**: Override auto-reply template (supports variables like `taskNumber`)\n- **Send default reply**: 3-state control - `null` (inherit), `true` (enabled), `false` (disabled)\n\n**How inheritance works:**\n- Providing a value overrides the workspace default\n- Omitting a field (or setting to `null` for some fields) inherits from workspace defaults\n- `sendDefaultReply` has special 3-state behavior: `null` = use workspace default, `true`/`false` = override\n- Updating settings automatically enables helpdesk for the account (`isEnabled: true`)\n\n**Example scenarios:**\n- Set only `defaultTaskPriority: High` → Account uses High priority, all other settings from workspace\n- Set `sendDefaultReply: false` → Disables auto-reply for this account only\n- Set `sendDefaultReply: null` → Account inherits workspace auto-reply setting\n\n**Error handling:**\nReturns 404 if the email account is not found or doesn't belong to the workspace.\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission.","operationId":"HelpdeskController_updateEmailAccountSettings","parameters":[{"name":"accountId","required":true,"in":"path","description":"Email account ID","schema":{"example":"account-uuid-123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailAccountHelpdeskSettingsDto"}}}},"responses":{"200":{"description":"Email account settings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountHelpdeskStatusResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update email account helpdesk settings","tags":["helpdesk"],"x-required-scopes":["api:write"]}},"/helpdesk/email-accounts/{accountId}/enable":{"post":{"description":"Enables helpdesk functionality for a specific email account. The account will use workspace default settings unless account-level overrides are configured.\n\n**What this does:**\n- Enables helpdesk processing for the specified email account\n- The account will start receiving and processing emails automatically\n- Uses workspace default settings if no account-specific overrides exist\n- Preserves any existing account-level settings if they were previously configured\n\n**How it works:**\n1. The system verifies the email account exists and belongs to the workspace\n2. Sets `isEnabled: true` for the account's helpdesk settings\n3. If no account settings exist, they are created with workspace defaults\n4. If account settings exist, only the `isEnabled` flag is updated\n5. The account becomes eligible for automated email pulling via cron jobs\n\n**After enabling:**\n- Emails sent to this account will be automatically pulled (every minute via cron)\n- Incoming emails will be converted to tasks using workspace defaults (or account overrides)\n- Auto-replies will be sent if configured in workspace or account settings\n- Tasks will be created with proper assignment, priority, and categorization\n\n**Error handling:**\nReturns 404 if the email account is not found or doesn't belong to the workspace.\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Workspace helpdesk must be enabled (`isHelpdeskEnabled: true`) for this to work.","operationId":"HelpdeskController_enableHelpdeskForAccount","parameters":[{"name":"accountId","required":true,"in":"path","description":"Email account ID","schema":{"example":"account-uuid-123","type":"string"}}],"responses":{"200":{"description":"Helpdesk enabled for email account successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountHelpdeskStatusResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Enable helpdesk for email account","tags":["helpdesk"],"x-required-scopes":["api:write"]}},"/helpdesk/email-accounts/{accountId}/disable":{"delete":{"description":"Disables helpdesk functionality for a specific email account. Any account-level override settings are preserved but not applied until re-enabled.\n\n**What this does:**\n- Disables helpdesk processing for the specified email account\n- Stops automatic email pulling and task creation for this account\n- Preserves all account-specific settings for future re-enabling\n- The account settings remain in the database but are not used\n\n**How it works:**\n1. The system verifies the email account exists and belongs to the workspace\n2. Sets `isEnabled: false` for the account's helpdesk settings\n3. All account-level overrides are preserved (task defaults, auto-reply, etc.)\n4. The account is removed from automated email pulling cron jobs\n\n**After disabling:**\n- No new emails will be pulled from this account\n- No tasks will be created from emails sent to this account\n- Existing tasks and email history remain unchanged\n- Settings can be restored by re-enabling the account\n\n**Use cases:**\n- Temporarily disable helpdesk for an account (e.g., during maintenance)\n- Stop processing emails for a specific account without losing configuration\n- Test different configurations by disabling/enabling accounts\n\n**Error handling:**\nReturns 404 if the email account is not found or doesn't belong to the workspace.\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission.","operationId":"HelpdeskController_disableHelpdeskForAccount","parameters":[{"name":"accountId","required":true,"in":"path","description":"Email account ID","schema":{"example":"account-uuid-123","type":"string"}}],"responses":{"200":{"description":"Helpdesk disabled for email account successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountHelpdeskStatusResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Email account not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Disable helpdesk for email account","tags":["helpdesk"],"x-required-scopes":["api:write"]}},"/helpdesk/organization-settings":{"get":{"description":"**This endpoint is deprecated.** Use `GET /helpdesk/organizations/:organizationId/settings` instead.\n\nThis endpoint was designed to retrieve organization-specific helpdesk settings, but requires an organization ID to function properly. Please use the endpoint with the organization ID in the path parameter.","operationId":"HelpdeskController_getOrganizationHelpdeskSettings","parameters":[],"responses":{"200":{"description":"Organization helpdesk settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationHelpdeskSettingsApiResponse"}}}},"400":{"description":"Organization ID is required"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization helpdesk settings (deprecated)","tags":["helpdesk"],"x-required-scopes":["api:read"]}},"/helpdesk/organizations/{organizationId}/settings":{"get":{"description":"Retrieves organization-specific helpdesk settings for a given organization.\n\n**What is Organization Helpdesk?**\nOrganizations can have their own helpdesk settings that override workspace defaults. This allows different organizations to have different task routing, priorities, and auto-reply configurations when emails are sent to the same helpdesk email account.\n\n**Settings priority (highest to lowest):**\n1. **Organization settings with mode='all'**: Override everything for all senders\n2. **Organization settings for member**: Apply when sender is an organization member (mode='onlyMembers')\n3. **Workspace email account settings**: Account-specific overrides at workspace level\n4. **Workspace defaults**: Base configuration for all helpdesk operations\n\n**What is returned:**\n- **Organization settings**: Organization-level configuration including:\n  - Organization ID and name\n  - Email account settings with organization-specific overrides\n  - Organization mode (`onlyMembers` or `all`)\n  - Task defaults, auto-reply configuration, and assignment rules\n- **Email account settings**: List of email accounts with organization-level settings:\n  - Each account shows organization-specific overrides\n  - Includes effective defaults (merged from workspace + organization settings)\n  - Shows which settings override workspace defaults\n\n**Organization modes:**\n- **`onlyMembers`**: Settings apply only when the email sender is a member of this organization\n- **`all`**: Settings apply to ALL emails sent to the account, regardless of sender\n\n**Error handling:**\nReturns 404 if the organization is not found or doesn't belong to the workspace.\n\n**Note:** This endpoint requires the `Organizations.edit` permission.","operationId":"HelpdeskController_getOrganizationHelpdeskSettingsById","parameters":[{"name":"organizationId","required":true,"in":"path","description":"Organization ID","schema":{"example":"org-uuid-123","type":"string"}}],"responses":{"200":{"description":"Organization helpdesk settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationHelpdeskSettingsApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Organization not found"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization helpdesk settings","tags":["helpdesk"],"x-required-scopes":["api:read"]},"put":{"description":"Updates organization-specific helpdesk settings. These settings override workspace defaults for the specified organization.\n\n**What can be configured:**\n- **Organization mode**: How settings apply (`onlyMembers` or `all`)\n  - `onlyMembers`: Settings apply only when sender is a member of this organization\n  - `all`: Settings apply to ALL emails sent to the account, regardless of sender\n- **Default task priority**: Priority level for organization helpdesk tasks\n- **Default task type**: Task type ID for organization helpdesk tasks\n- **Default task status**: Task status ID for organization helpdesk tasks\n- **Auto-assign user**: Optional user ID to automatically assign tasks to\n- **Default reply body**: HTML template for automatic replies (supports variables like `taskNumber`)\n- **Send default reply**: Whether to automatically send replies to new email senders\n\n**How it works:**\n1. Organization settings override workspace defaults when emails are processed\n2. Settings apply based on the organization mode:\n   - `onlyMembers`: Only when sender is an organization member\n   - `all`: For all emails to the account (highest priority)\n3. Null fields inherit from workspace email account settings or workspace defaults\n4. Multiple organizations can have settings for the same email account\n\n**Settings resolution example:**\nIf an email is sent to `support@company.com`:\n- First check: Any org with mode='all'? → Use that org's settings\n- Second check: Is sender a member? → Use their org's settings (if mode='onlyMembers')\n- Third check: Workspace email account settings\n- Final fallback: Workspace defaults\n\n**Error handling:**\nReturns 404 if the organization is not found or doesn't belong to the workspace.\n\n**Note:** This endpoint requires the `Organizations.edit` permission. Organization settings provide fine-grained control over helpdesk behavior per organization.","operationId":"HelpdeskController_updateOrganizationHelpdeskSettings","parameters":[{"name":"organizationId","required":true,"in":"path","description":"Organization ID","schema":{"example":"org-uuid-123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationHelpdeskSettingsDto"}}}},"responses":{"200":{"description":"Organization helpdesk settings updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationHelpdeskSettingsApiResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Organization not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization helpdesk settings","tags":["helpdesk"],"x-required-scopes":["api:write"]}},"/administration/appearance-settings":{"get":{"description":"Retrieves the current workspace appearance settings.\n\n**What is returned:**\n- **Main workspace color**: The primary color theme used throughout the workspace\n- **Logo URL**: Public URL for the workspace logo (shown in light theme)\n- **Logo dark theme URL**: Public URL for the workspace logo used in dark theme\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Appearance settings affect how the workspace looks to all users in the workspace.","operationId":"AppearanceSettingsController_getAppearanceSettings","parameters":[],"responses":{"200":{"description":"Appearance settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppearanceSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get workspace appearance settings","tags":["administration","appearance-settings"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates workspace appearance settings. All fields are optional - only provided fields will be updated.\n\n**What can be updated:**\n- **Main workspace color**: Changes the primary color theme used throughout the workspace\n- **Logo**: Upload a logo file for light theme display\n- **Logo dark theme**: Upload a separate logo file optimized for dark theme display\n\n**To upload logo files:**\n1. Call POST /api/public/workspace/upload to upload each logo file\n2. Get the file ID from the response\n3. Use that file ID in the `logoId` or `logoDarkThemeId` field\n4. Set to `null` to remove an existing logo\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect how the workspace appears to all users. The system validates that provided file IDs exist and belong to the workspace.","operationId":"AppearanceSettingsController_updateAppearanceSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAppearanceSettingsDto"}}}},"responses":{"200":{"description":"Appearance settings updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"File not found - Invalid logoId or logoDarkThemeId provided"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update workspace appearance settings","tags":["administration","appearance-settings"],"x-required-scopes":["api:write"]}},"/administration/basic-settings":{"get":{"description":"Retrieves the current basic workspace settings.\n\n**What is returned:**\n- **Workspace identification**: ID and brand color\n- **Localization**: Default language, country of operation, timezone\n- **Attendance tracking**: Whether attendance is enabled, color thresholds for time tracking quality\n- **Default working hours**: Default start and end times for the workspace\n- **Time tracker**: Reminder interval settings\n- **Sprint configuration**: Sprint duration, start day, and end day\n- **Journal notifications**: User IDs who receive notifications for journal entries\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. These settings affect how the entire workspace operates and are used as defaults throughout the system.","operationId":"BasicSettingsController_getBasicSettings","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBasicSettingsDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get workspace basic settings","tags":["administration","basic-settings"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates workspace basic settings. All fields are optional - only provided fields will be updated.\n\n**What can be updated:**\n- **Brand color**: Hex color code for workspace branding\n- **Default language**: Default language for the workspace (e.g., \"en\", \"de\")\n- **Country of operation**: ISO country code (e.g., \"US\", \"DE\")\n- **Timezone**: IANA timezone identifier (e.g., \"America/New_York\", \"Europe/Berlin\")\n- **Attendance tracking**: Enable or disable attendance tracking for employees\n- **Time tracking thresholds**: Percentage thresholds for marking time tracking as \"good\" or \"bad\"\n- **Default working hours**: Default start and end times for employees\n- **Time tracker reminders**: Interval in minutes for reminding users to track time (5-120 minutes)\n- **Sprint configuration**: Sprint duration in days, start day (1=Monday), and end day\n- **Journal notifications**: List of user IDs who should receive notifications for journal entries\n\n**Validation rules:**\n- `timeColorBadPercent` must be less than `timeColorGoodPercent`\n- `defaultTimeTo` must be after `defaultTimeFrom`\n- `timeTrackerAnnoyanceInMin` must be between 5 and 120\n- `sprintStartDay` and `sprintEndDay` must be between 1 (Monday) and 7 (Sunday)\n\n**Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect the entire workspace and all users.","operationId":"BasicSettingsController_patchBasicSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchBasicSettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update workspace basic settings","tags":["administration","basic-settings"],"x-required-scopes":["api:write"]}},"/holidays/settings":{"get":{"description":"Retrieves the workspace holiday configuration, including the default holiday country/region and all available holiday sources.\n\n**What are Holiday Settings?**\nHoliday settings determine which regional holiday rules the system uses for capacity planning. Leadtime needs to know when employees are not available due to regional holidays, which is essential for accurate capacity management.\n\n**What is returned:**\n- `defaultHolidayCode`: The default holiday country/region code for the workspace (e.g., \"US\", \"DE\", \"US-CA\")\n- `availableCountries`: List of all available holiday countries and states with their codes and localized names\n\n**How it works:**\n- The default holiday code is automatically applied to all new employees\n- Individual employees can have different holiday rules set in their profile\n- Holidays are displayed in the team calendar and used for capacity calculations\n- Use the `language` query parameter to get country names in a specific language (default: \"en\")\n\n**Note:** This endpoint requires the `manageSettings` permission.","operationId":"HolidaysController_getHolidaySettings","parameters":[{"name":"language","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Holiday settings retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidaySettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get holiday settings","tags":["holidays","time-management"],"x-required-scopes":["api:read"]},"put":{"description":"Updates the default holiday country/region code for the workspace.\n\n**What this does:**\nSets the default holiday source that will be automatically applied to all new employees. This ensures consistent holiday rules across your workspace while still allowing individual customization.\n\n**Important:**\n- This only affects new employees - existing employees keep their current holiday settings\n- The country code must be one of the available codes from `GET /holidays/countries`\n- Examples: \"US\" (United States), \"DE\" (Germany), \"US-CA\" (California), \"AT\" (Austria)\n\n**Use cases:**\n- Setting up a new workspace with a primary location\n- Changing the default for a workspace that has moved or expanded to a new region\n- Standardizing holiday rules across the organization","operationId":"HolidaysController_updateHolidaySettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateHolidaySettingsDto"}}}},"responses":{"200":{"description":"Holiday settings updated successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update holiday settings","tags":["holidays","time-management"],"x-required-scopes":["api:write"]}},"/holidays/countries":{"get":{"description":"Retrieves the complete list of available holiday countries and states that can be used for holiday configurations.\n\n**What is returned:**\n- List of all supported holiday regions with:\n  - `code`: Country/region code (e.g., \"US\", \"DE\", \"US-CA\", \"AT\")\n  - `name`: Localized country/region name (e.g., \"United States\", \"Germany\", \"California\", \"Austria\")\n\n**How to use:**\n- Use these codes when setting default holiday settings or individual employee holiday rules\n- Country codes follow ISO 3166-1 alpha-2 format (e.g., \"US\", \"DE\")\n- State codes use format \"COUNTRY-STATE\" (e.g., \"US-CA\" for California, \"US-NY\" for New York)\n- Use the `language` query parameter to get country names in a specific language (default: \"en\")\n\n**Note:** This list includes all regions for which Leadtime has official holiday data. Holidays are automatically loaded from official sources for these regions.","operationId":"HolidaysController_getHolidayCountries","parameters":[{"name":"language","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Holiday countries retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HolidayCountryDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get available holiday countries","tags":["holidays","time-management"],"x-required-scopes":["api:read"]}},"/holidays/years":{"get":{"description":"Retrieves the complete holiday configuration for a specific year and country/region.\n\n**What is returned:**\nThe endpoint returns one of three types of holiday configurations:\n\n1. **Default** (`type: \"default\"`): Official holidays from the holiday data source\n   - No `id` field (not a custom year)\n   - Cannot be modified directly\n   - Used when no custom configuration exists\n\n2. **Custom** (`type: \"custom\"`): Workspace-specific holiday configuration\n   - Has an `id` field (UUID)\n   - Can be modified (add, edit, delete holidays)\n   - Created when you customize holidays for a year\n   - May have a `validTill` year that controls propagation to future years\n\n3. **Derived** (`type: \"derived\"`): Configuration inherited from a previous custom year\n   - Has an `id` field (UUID)\n   - Shows `derivedFrom` year that it was copied from\n   - Created automatically when a custom year's `validTill` extends to future years\n   - Can be modified like custom years\n\n**Holiday types:**\nEach holiday in the list can be:\n- **Fixed**: A specific date (e.g., \"2024-12-25\" for Christmas)\n- **Rule**: A computed date based on a rule (e.g., \"25 december\" or \"easter monday\")\n\n**Response fields:**\n- `id`: Holiday year ID (only for custom/derived types)\n- `year`: The year this configuration applies to\n- `country`: Country/region code\n- `type`: Configuration type (\"default\", \"custom\", or \"derived\")\n- `derivedFrom`: Source year for derived configurations\n- `validTill`: Last year this configuration applies to (null = indefinite)\n- `holidays`: Array of all holidays with name, date, type, rule, and substitute flag\n\n**Use cases:**\n- Displaying holidays in a calendar view\n- Checking if a year has been customized\n- Getting the complete holiday list for capacity planning\n- Understanding the relationship between years (derived configurations)","operationId":"HolidaysController_getHolidayYear","parameters":[{"name":"year","required":true,"in":"query","description":"The year to retrieve holidays for. Must be 1900 or later. The endpoint will return the appropriate configuration type (default, custom, or derived) for this year.","schema":{"example":2024,"type":"number"}},{"name":"country","required":true,"in":"query","description":"Country or region code (e.g., \"US\", \"DE\", \"US-CA\"). Must be one of the available codes from `GET /holidays/countries`.","schema":{"example":"US","type":"string"}}],"responses":{"200":{"description":"Holiday year retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayYearResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get holiday year","tags":["holidays","time-management"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a custom holiday year configuration for the workspace, allowing you to customize holidays for a specific year and country/region.\n\n**What this does:**\nCreates a workspace-specific holiday configuration by copying the default holidays for the specified year and country. Once created, you can add, modify, or delete holidays using the holiday days endpoints.\n\n**Important behavior:**\n- If a custom year already exists for the specified year/country, it will be **deleted and recreated**\n- This effectively resets the year to default holidays\n- All existing custom holidays for that year will be lost\n- Use this endpoint to start customizing a year or to reset it back to defaults\n\n**Validity period (`validTill`):**\nThe `validTill` field controls how the custom configuration propagates to future years:\n- If `validTill` is set (e.g., 2026), the configuration applies to all years from the specified year through `validTill`\n- If `validTill` is `null`, the configuration applies indefinitely to future years\n- Future years will automatically be created as \"derived\" configurations\n- You can update `validTill` later using `PUT /holidays/years/:yearId`\n\n**Use cases:**\n- Adding company-specific holidays (e.g., \"Company Outing\", \"Festivus\")\n- Removing holidays that don't apply to your organization\n- Modifying holiday dates for your specific needs\n- Resetting a customized year back to defaults\n\n**Next steps:**\nAfter creating a custom year, use:\n- `POST /holidays/days` to add new holidays\n- `PUT /holidays/days/:dayId` to modify existing holidays\n- `DELETE /holidays/days/:dayId` to remove holidays","operationId":"HolidaysController_createHolidayYear","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHolidayYearDto"}}}},"responses":{"201":{"description":"Custom holiday year created successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create custom holiday year","tags":["holidays","time-management"],"x-required-scopes":["api:write"]}},"/holidays/years/{yearId}":{"put":{"description":"Updates the validity period (`validTill`) of a custom holiday year configuration.\n\n**What this does:**\nChanges how long a custom holiday configuration applies to future years. This is useful when you want to extend or limit the scope of your customizations.\n\n**How `validTill` works:**\n- **Set to a specific year** (e.g., `2026`): The configuration applies from the year's start year through 2026\n  - Future years within this range will be automatically created as \"derived\" configurations\n  - Years beyond `validTill` will use default holidays\n\n- **Set to `null`**: The configuration applies indefinitely to all future years\n  - All future years will automatically inherit this configuration as \"derived\" years\n  - Useful for permanent company holidays that should always apply\n\n**Example scenarios:**\n1. **Temporary change**: You add a special holiday for 2024-2025 only\n   - Create year 2024 with `validTill: 2025`\n   - Both 2024 and 2025 will use the custom configuration\n   - 2026 and beyond will use defaults\n\n2. **Permanent company holiday**: You add \"Company Foundation Day\" that should always be a holiday\n   - Create year 2024 with `validTill: null`\n   - All future years will automatically include this holiday\n\n3. **Extending validity**: You previously set `validTill: 2025` but want to extend to 2027\n   - Update the year with `validTill: 2027`\n   - Years 2026 and 2027 will now also use the custom configuration\n\n**Note:** This endpoint only works on custom holiday years (those with an `id`). Default years cannot be updated - you must create a custom year first using `POST /holidays/years`.","operationId":"HolidaysController_updateHolidayYear","parameters":[{"name":"yearId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateHolidayYearDto"}}}},"responses":{"200":{"description":"Holiday year updated successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update holiday year","tags":["holidays","time-management"],"x-required-scopes":["api:write"]},"delete":{"description":"**Note:** This endpoint is not implemented. To reset a custom holiday year back to defaults, use `POST /holidays/years` instead.\n\n**How to reset a year:**\nTo delete a custom holiday year and return to default holidays:\n1. Call `POST /holidays/years` with the same `year` and `country` values\n2. This will delete the existing custom year and recreate it with default holidays\n3. All custom holidays for that year will be removed\n\n**Why this approach?**\nThe holiday service uses a \"recreate\" pattern where creating a custom year automatically deletes any existing custom year for the same year/country combination. This ensures a clean reset to defaults while maintaining data consistency.","operationId":"HolidaysController_deleteHolidayYear","parameters":[{"name":"yearId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Holiday year deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete custom holiday year","tags":["holidays","time-management"],"x-required-scopes":["api:write"]}},"/holidays/days":{"post":{"description":"Adds a new holiday day to a custom holiday year configuration.\n\n**What this does:**\nCreates a new holiday in a custom holiday year. This allows you to add company-specific holidays that don't exist in the default holiday data (e.g., \"Company Outing\", \"Festivus\", \"Team Building Day\").\n\n**Holiday types:**\nOnly **Fixed** holidays can be created via this endpoint:\n- **Fixed**: A specific date that doesn't change (e.g., \"2024-12-25\" for Christmas)\n- **Rule-based**: Holidays computed from rules (e.g., \"25 december\", \"easter monday\") come from default definitions\n  - Rule-based holidays can only be edited, not created\n  - They exist in default holiday configurations and can be modified in custom years\n\n**Requirements:**\n- The `yearId` must belong to a custom holiday year (created via `POST /holidays/years`)\n- You cannot add holidays to default years - create a custom year first\n- The `date` must be in ISO 8601 format (e.g., \"2024-12-25\")\n- The `name` should be descriptive (e.g., \"Company Foundation Day\")\n\n**Use cases:**\n- Adding company-specific holidays\n- Adding regional holidays not in the default data\n- Creating special event days that should be treated as holidays\n\n**Example:**\nTo add \"Company Outing\" on June 15, 2024:\n```json\n{\n  \"yearId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"name\": \"Company Outing\",\n  \"type\": \"Fixed\",\n  \"date\": \"2024-06-15\"\n}\n```","operationId":"HolidaysController_createHolidayDay","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHolidayDayDto"}}}},"responses":{"201":{"description":"Holiday day created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayDayResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Add holiday day","tags":["holidays","time-management"],"x-required-scopes":["api:write"]}},"/holidays/days/{dayId}":{"put":{"description":"Updates an existing holiday day in a custom holiday year configuration.\n\n**What this does:**\nModifies a holiday's name, type, date, or rule. This allows you to customize holidays from the default set or modify holidays you've previously added.\n\n**Holiday types:**\nYou can update holidays to be either type:\n\n1. **Fixed** (`type: \"Fixed\"`):\n   - A specific date that doesn't change\n   - Requires a `date` field in ISO 8601 format (e.g., \"2024-12-25\")\n   - Example: \"Christmas Day\" on December 25\n\n2. **Rule** (`type: \"Rule\"`):\n   - A computed date based on a rule string\n   - Requires a `rule` field (e.g., \"25 december\", \"easter monday\", \"first monday in september\")\n   - The date is automatically computed from the rule for each year\n   - If converting from Fixed to Rule, you must provide a `rule` (or it will use the existing rule if available)\n\n**Important constraints:**\n- You cannot convert a Fixed holiday to Rule without providing a `rule`\n- If the holiday already has a rule and you're converting to Rule type, the existing rule will be preserved if you don't provide one\n- The holiday must belong to a custom year (not a default year)\n- The `yearId` is automatically determined from the existing holiday\n\n**Use cases:**\n- Renaming a holiday (e.g., \"Christmas\" → \"Holiday Break\")\n- Changing a holiday date\n- Converting a fixed holiday to a rule-based one (e.g., \"First Monday in September\" for Labor Day)\n- Modifying rule-based holidays from the default set\n\n**Example - Converting Fixed to Rule:**\n```json\n{\n  \"name\": \"Labor Day\",\n  \"type\": \"Rule\",\n  \"rule\": \"first monday in september\"\n}\n```","operationId":"HolidaysController_updateHolidayDay","parameters":[{"name":"dayId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateHolidayDayDto"}}}},"responses":{"200":{"description":"Holiday day updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayDayResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update holiday day","tags":["holidays","time-management"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes a holiday day from a custom holiday year configuration.\n\n**What this does:**\nRemoves a holiday from the custom year configuration. The holiday is soft-deleted, meaning it's marked as deleted but not permanently removed from the database.\n\n**Important:**\n- Only holidays in custom years can be deleted\n- You cannot delete holidays from default years - create a custom year first\n- This is useful for removing holidays that don't apply to your organization\n- The holiday will no longer appear in holiday lists or affect capacity calculations\n\n**Use cases:**\n- Removing regional holidays that don't apply to your organization\n- Removing religious holidays that aren't observed by your company\n- Cleaning up holidays you previously added but no longer need\n\n**Note:** If you want to restore a deleted holiday, you can either:\n- Recreate the custom year using `POST /holidays/years` (resets to defaults)\n- Add the holiday back using `POST /holidays/days`","operationId":"HolidaysController_deleteHolidayDay","parameters":[{"name":"dayId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Holiday day deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete holiday day","tags":["holidays","time-management"],"x-required-scopes":["api:write"]}},"/contacts/grid":{"get":{"description":"Returns a paginated, filterable, and sortable grid of all contacts in the workspace.\n\n**What are Contacts?**\nThe contacts grid provides a unified view of all people in your workspace:\n- **Employees**: Internal team members from your own company\n- **Organization Members**: External contacts from partner or customer organizations\n\nThis unified view allows you to search, filter, and manage all contacts in one place, regardless of whether they are internal employees or external contacts.\n\n**What is returned:**\n- Contact identification (ID, name, type)\n- Contact information (email, phone, position, title, degree)\n- Organization information (for organization members)\n- Complete address details\n- Employee-specific fields (employment mode, entry/exit dates, tax class)\n- Organization member-specific fields (influence level, attitude, personality type)\n- Status information (active, can login)\n- Personal details (birth date, gender, avatar)\n\n**Filtering and search:**\n- Quick search across name, email, phone, position, organization, and address fields\n- Advanced filtering by contact type, status, organization, and many other fields\n- Sorting by any sortable field\n- Server-side pagination for large contact lists\n\n**Note:** This endpoint requires the `Contacts.canSeeContacts` permission. The contacts grid is the foundation for the Address Book feature, which can be synced via CardDAV or exported as vCard.","operationId":"ContactsController_getContactsGrid","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: firstName, lastName, email, phone, position, organizationName, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Contact ID\n- **type** (set): Contact type (EMPLOYEE, ORGANIZATION_MEMBER)\n- **firstName** (string): First name\n- **lastName** (string): Last name\n- **email** (string): Email address\n- **phone** (string): Phone number\n- **position** (string): Job position\n- **title** (string): Title (e.g., Dr., Prof.)\n- **degree** (string): Academic degree\n- **organizationId** (string): Organization ID (for organization members)\n- **organizationName** (string): Organization name (for organization members)\n- **addressStreet** (string): Street address\n- **addressZip** (string): ZIP/Postal code\n- **addressCity** (string): City\n- **addressCountry** (string): Country\n- **addressHouseNumber** (string): House number\n- **avatarId** (string): Avatar file ID\n- **isActive** (boolean): Whether contact is active\n- **canLogin** (boolean): Whether contact can login to the system\n- **employmentMode** (string): Employment mode (for employees)\n- **entryDate** (date): Entry date (for employees)\n- **exitDate** (date): Exit date (for employees)\n- **incomeTaxClass** (string): Income tax class (for employees)\n- **influenceLevel** (string): Influence level (for organization members)\n- **attitudeLevel** (string): Attitude level (for organization members)\n- **personalityType** (string): Personality type (for organization members)\n- **socialNetworkLink** (string): Social network profile link (for organization members)\n- **birthDate** (date): Birth date\n- **gender** (string): Gender\n- **createdAt** (date): Creation timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Contact ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Contact type (EMPLOYEE, ORGANIZATION_MEMBER) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **firstName** (string): First name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **lastName** (string): Last name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **phone** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **position** (string): Job position (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **title** (string): Title (e.g., Dr., Prof.) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **degree** (string): Academic degree (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationId** (string): Organization ID (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationName** (string): Organization name (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressStreet** (string): Street address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressZip** (string): ZIP/Postal code (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressHouseNumber** (string): House number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **avatarId** (string): Avatar file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **isActive** (boolean): Whether contact is active (boolean filter). Available comparisons: equal, not_equal\n- **canLogin** (boolean): Whether contact can login to the system (boolean filter). Available comparisons: equal, not_equal\n- **employmentMode** (string): Employment mode (for employees) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **entryDate** (date): Entry date (for employees) (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **exitDate** (date): Exit date (for employees) (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **incomeTaxClass** (string): Income tax class (for employees) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **influenceLevel** (string): Influence level (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **attitudeLevel** (string): Attitude level (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **personalityType** (string): Personality type (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **socialNetworkLink** (string): Social network profile link (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **birthDate** (date): Birth date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **gender** (string): Gender (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Contact ID\n- **type**: Contact type (EMPLOYEE, ORGANIZATION_MEMBER)\n- **firstName**: First name\n- **lastName**: Last name\n- **name**: Full name (firstName + lastName, virtual field)\n- **position**: Job position\n- **organizationId**: Organization ID (for organization members)\n- **organizationName**: Organization name (for organization members)\n- **addressCity**: City\n- **addressCountry**: Country\n- **isActive**: Whether contact is active\n- **canLogin**: Whether contact can login to the system\n- **employmentMode**: Employment mode (for employees)\n- **entryDate**: Entry date (for employees)\n- **exitDate**: Exit date (for employees)\n- **influenceLevel**: Influence level (for organization members)\n- **attitudeLevel**: Attitude level (for organization members)\n- **personalityType**: Personality type (for organization members)\n- **birthDate**: Birth date\n- **createdAt**: Creation timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** name (asc)","schema":{"example":"[{\"field\":\"name\",\"direction\":\"asc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, type, firstName, lastName, name, email, phone, position, title, degree, organizationId, organizationName, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber, avatarId, isActive, canLogin, employmentMode, entryDate, exitDate, incomeTaxClass, influenceLevel, attitudeLevel, personalityType, socialNetworkLink, birthDate, gender, createdAt","schema":{"example":"id,type","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved contacts grid"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get contacts grid","tags":["contacts"],"x-required-scopes":["api:read"]}},"/administration/custom-icons":{"get":{"description":"Retrieves all custom icons configured for the workspace.\n\n**What are Custom Icons?**\nCustom icons are user-uploaded symbols (images) that can be used throughout the workspace to visually organize and tag content. They help make information easier to grasp quickly.\n\n**Where can custom icons be used:**\n- Projects\n- Tickets/Tasks\n- Documents\n- Organization profiles\n- Employee profiles\n\n**What is returned:**\n- Icon ID and name (e.g., `:company_logo:` or `:team_sales:`)\n- Image file ID and public URL\n- Author information (who created the icon)\n- Creation and last update timestamps\n\n**Note:** This endpoint requires the `CustomIcons.create` permission. All custom icons are available to all users in the workspace.","operationId":"CustomIconsController_getCustomIcons","parameters":[],"responses":{"200":{"description":"Custom icons retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomIconResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List custom icons","tags":["administration","custom-icons"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new custom icon for the workspace.\n\n**What are Custom Icons?**\nCustom icons are user-uploaded symbols (images) that can be used throughout the workspace to visually organize and tag content. They help make information easier to grasp quickly.\n\n**How to create a custom icon:**\n1. Upload an image file (PNG or SVG format) using POST /api/public/workspace/upload\n2. Get the file ID from the upload response\n3. Provide a unique icon name (e.g., `:company_logo:`, `:team_sales:`, `:lt_editor:`)\n4. Use the file ID in the `imageId` field\n\n**Icon name requirements:**\n- Must be alphanumeric characters and underscores only\n- Cannot conflict with default emoji names\n- Format: typically wrapped in colons (e.g., `:icon_name:`)\n\n**Note:** This endpoint requires the `CustomIcons.create` permission. Once created, the icon is available to all users in the workspace and can be used in projects, tickets, documents, and profiles.","operationId":"CustomIconsController_createCustomIcon","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomIconDto"}}}},"responses":{"201":{"description":"Custom icon created successfully","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create custom icon","tags":["administration","custom-icons"],"x-required-scopes":["api:write"]}},"/administration/custom-icons/{id}":{"put":{"description":"Updates an existing custom icon by replacing its image.\n\n**What can be updated:**\n- **Image only**: The icon image can be replaced with a new one\n- **Name is readonly**: The icon name cannot be changed after creation\n\n**How to update a custom icon:**\n1. Upload a new image file (PNG or SVG format) using POST /api/public/workspace/upload\n2. Get the file ID from the upload response\n3. Use the file ID in the `imageId` field\n\n**Note:** This endpoint requires the `CustomIcons.create` permission. The icon name remains unchanged - only the image is updated. The updated icon will be reflected throughout the workspace wherever it's used.","operationId":"CustomIconsController_updateCustomIcon","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomIconDto"}}}},"responses":{"200":{"description":"Custom icon updated successfully","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update custom icon","tags":["administration","custom-icons"],"x-required-scopes":["api:write"]},"delete":{"description":"Permanently deletes a custom icon from the workspace.\n\n**What happens:**\n- The custom icon is permanently removed from the workspace\n- The icon will no longer be available for use in projects, tickets, documents, or profiles\n- Any existing uses of the icon may display as missing/broken\n- This action cannot be undone\n\n**Note:** This endpoint requires the `CustomIcons.create` permission. Use with caution as deleting an icon may affect content that references it.","operationId":"CustomIconsController_deleteCustomIcon","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Custom icon deleted successfully","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete custom icon","tags":["administration","custom-icons"],"x-required-scopes":["api:write"]}},"/timebookings/timeGrid":{"get":{"description":"Retrieves a paginated, filterable grid of all time booking entries in the workspace.\n\n**What are Time Bookings?**\nTime bookings (also called time logs or time entries) are records of working hours that employees have logged to specific tasks or projects. They document how much time was spent on different activities, which is essential for:\n- Project planning and resource allocation\n- Billing customers accurately\n- Analyzing productivity and team workload\n- Understanding where time is being spent in the organization\n\n**What data is returned:**\nThe grid returns time booking entries with the following information:\n- **Date**: When the time was logged\n- **Hours**: Amount of time logged (decimal format, e.g., 8.5 for 8 hours 30 minutes)\n- **Comment**: Optional notes about the work performed\n- **Project ID**: The project the time was logged to\n- **Task ID**: The specific task (if time was logged to a task rather than just a project)\n- **User ID**: Which employee logged the time\n- **Activity ID**: The type of work performed (e.g., development, research, management)\n- **Value Group**: The project value group for categorization\n\n**Response includes:**\n- Paginated list of time booking items\n- Total count of matching entries\n- **totalHours**: Sum of all hours across filtered entries (useful for reporting)\n- Current page and page size\n\n**Filtering and sorting:**\nYou can filter by date range, project, task, user, activity, or value group. Quick search is available on comments. Results are sorted by date (newest first) by default.\n\n**Permissions:**\nRequires either \"seeLoggedTimeDetails\" (to see your own time) or \"seeCompanyLoggedTime\" (to see all company time).","operationId":"TimebookingsController_getTimeGrid","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: comment","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **date** (date): Date of the time log entry\n- **hours** (number): Hours logged\n- **comment** (string): Comment for the time log entry\n- **projectId** (set): Project ID\n- **taskId** (set): Task ID\n- **userId** (set): User ID who logged the time\n- **activityId** (set): Activity ID\n- **valueGroup** (set): Project value group\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **date** (date): Date of the time log entry (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **hours** (number): Hours logged (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty\n- **comment** (string): Comment for the time log entry (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **taskId** (set): Task ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **userId** (set): User ID who logged the time (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **activityId** (set): Activity ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **valueGroup** (set): Project value group (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **date**: Date of the time log entry\n- **hours**: Hours logged\n- **projectId**: Project ID\n- **taskId**: Task ID\n- **userId**: User ID who logged the time\n- **activityId**: Activity ID\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** date (desc)","schema":{"example":"[{\"field\":\"date\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** date, hours, comment, projectId, taskId, userId, activityId, valueGroup","schema":{"example":"date,hours","type":"array","items":{}}},{"name":"group","required":false,"in":"query","description":"Group time bookings by date. When set, response includes groupMeta with fragment/full totals per group. Use \"none\" for ungrouped list. Default: none.","schema":{"enum":["day","week","month","none"],"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved time bookings grid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimebookingsGridResponseDto"}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get time bookings grid","tags":["timebookings","time-tracking"],"x-required-scopes":["api:read"]}},"/timebookings":{"post":{"description":"Creates a new time booking entry to record working hours spent on a task or project.\n\n**What is a Time Booking?**\nA time booking is a record of working hours that you have spent on a specific task or project. Time bookings are used for retroactive time tracking - documenting work that has already been completed.\n\n**Two types of time bookings:**\n1. **Task-based**: Time logged to a specific task within a project. Use this when you want to track time for a particular work item (e.g., \"Fix bug in login form\"). The time automatically counts toward the parent project.\n2. **Project-based**: Time logged directly to a project without a specific task. Use this for general project activities that are not tied to a particular task (e.g., \"Marketing research for Q1 campaign\").\n\n**Required information:**\n- **Type**: Either \"task\" or \"project\"\n- **Task ID** (if type is \"task\"): The UUID of the task you worked on\n- **Project ID** (if type is \"project\"): The UUID of the project\n- **Activity ID**: The type of work performed (e.g., development, design, management). Activities are defined in your workspace settings.\n- **Hours**: The amount of time spent (decimal format, e.g., 2.5 for 2 hours 30 minutes)\n- **Date**: The date when the work was performed (ISO date format: YYYY-MM-DD)\n\n**Optional information:**\n- **Minutes**: Additional minutes (0-59) to add to the hours. If you provide both hours and minutes, they are combined (e.g., hours: 2, minutes: 30 = 2.5 hours total).\n- **Comment**: Notes about what was accomplished during this time\n\n**How it works:**\n- Time bookings are always created for the authenticated user (you cannot log time for other users)\n- When logging time to a task, the system automatically links it to the task's parent project\n- The time entry is immediately available in time tracking reports and project analytics\n- Hours and minutes are combined: if you provide hours: 2 and minutes: 30, the total is 2.5 hours\n\n**Example use cases:**\n- Documenting time spent on a completed feature\n- Recording billable hours for client projects\n- Tracking time for internal projects and activities\n- Completing time logs at the end of the day or week\n\n**Note:** For real-time time tracking while working, use the Time Tracker feature instead of manual time bookings.","operationId":"TimebookingsController_createTimeBooking","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTimeBookingDto"}}}},"responses":{"201":{"description":"Time booking created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid request data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing permission to log time"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new time booking","tags":["timebookings","time-tracking"],"x-required-scopes":["api:write"]}},"/timebookings/{id}":{"patch":{"description":"Updates an existing time booking entry. You can modify any field of the time booking.\n\n**Who can update time bookings:**\n- You can always update your own time bookings\n- If you have the \"editAnyTimeLog\" permission, you can update any time booking in the workspace\n- Otherwise, you cannot update time bookings created by other users\n\n**What can be updated:**\nAll fields are optional in the update request. Only provide the fields you want to change:\n- **Type**: Change between task-based and project-based booking\n- **Task ID**: Switch to a different task (if type is \"task\")\n- **Project ID**: Switch to a different project (if type is \"project\")\n- **Activity ID**: Change the activity type\n- **Hours**: Adjust the logged hours\n- **Minutes**: Adjust additional minutes (0-59)\n- **Comment**: Update the comment text\n- **Date**: Change the date when the work was performed\n\n**How it works:**\n- Fields not provided in the update request will keep their existing values\n- If you change the type from \"task\" to \"project\" (or vice versa), make sure to provide the appropriate ID field\n- Hours and minutes are combined: if you provide both, they are added together\n- The system validates that the time booking exists and belongs to your workspace\n\n**Common use cases:**\n- Correcting a typo in hours or comment\n- Moving time from one task to another\n- Changing the activity type for better categorization\n- Updating the date if it was logged incorrectly","operationId":"TimebookingsController_updateTimeBooking","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTimeBookingDto"}}}},"responses":{"200":{"description":"Time booking updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid request data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing permission to edit this time booking"},"404":{"description":"Time booking not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update an existing time booking","tags":["timebookings","time-tracking"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a time booking entry. The entry is marked as deleted but not permanently removed from the database.\n\n**Who can delete time bookings:**\n- You can always delete your own time bookings\n- If you have the \"deleteAnyTimeLog\" permission, you can delete any time booking in the workspace\n- Otherwise, you cannot delete time bookings created by other users\n\n**How it works:**\n- The time booking is soft deleted (marked as deleted, not permanently removed)\n- Deleted entries are no longer visible in time tracking grids and reports\n- The system validates that the time booking exists and belongs to your workspace\n- If the time booking was linked to a task, the task's total time will be recalculated\n\n**Use cases:**\n- Removing accidentally created duplicate entries\n- Deleting time logged to the wrong task or project\n- Cleaning up test or incorrect time entries","operationId":"TimebookingsController_deleteTimeBooking","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Time booking deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Missing permission to delete this time booking"},"404":{"description":"Time booking not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a time booking","tags":["timebookings","time-tracking"],"x-required-scopes":["api:write"]}},"/time/timetable":{"get":{"description":"Retrieves comprehensive time table data for employees over a specified date range. The time table provides a calendar view showing attendance, time bookings, vacations, sickness, holidays, and work hours for team members.\n\n**What is the Time Table?**\nThe time table is a unified calendar view that combines multiple time-related data sources:\n- Attendance tracking (clock in/out times, total hours, mood)\n- Time bookings (hours logged on projects and tasks)\n- Vacation requests (pending and approved)\n- Sickness records\n- Public holidays (based on employee holiday codes)\n- Work day configuration (working days, work day length)\n- Chargeable time goals and actuals\n\n**Required Permission:**\nUser must be an Employee type. Additionally, one of the following time table viewing permissions is required:\n- TimeTablePermissions.viewAll: View all employees\n- TimeTablePermissions.viewOwn: View own data only\n- TimeTablePermissions.viewTeams: View own team members\n\n**User Filtering Options:**\nThe usersFilter parameter controls which employees are included in the response:\n- \"all\": Returns all employees in the workspace. Requires TimeTablePermissions.viewAll permission.\n- \"me\": Returns only the authenticated user. Requires TimeTablePermissions.viewOwn or TimeTablePermissions.viewTeams permission.\n- \"team:{teamId}\": Returns all members of the specified team. Requires TimeTablePermissions.viewTeams permission, and the user must be a member of that team.\n\n**Data Visibility Rules:**\nThe response includes different data fields based on permissions and workspace settings:\n- spentHours and chargeableTime: Only included if user has Tasks.seeLoggedTime permission\n- Attendance data (timeFrom, timeTo, totalHours, mood): Only included if workspace has attendanceEnabled flag set\n- If attendance is disabled, totalHours shows time booking data instead\n- Returns empty object {} if user has no time table viewing permissions\n\n**Date Handling:**\n- Dates must be in ISO 8601 format (YYYY-MM-DD)\n- Dates are normalized to UTC at midnight\n- If dateStart > dateEnd, they are automatically swapped\n- Response includes all days in the range, including weekends\n\n**Response Structure:**\nReturns an object keyed by userId. Each key contains:\n- employeeId: Employee UUID\n- userId: User UUID\n- name: Employee full name\n- days: Array of day objects, one for each date in the range\n\n**Day Object Fields:**\nEach day object contains:\n- type: 0 for Attendance mode, 1 for TimeBooking mode\n- date: ISO date string\n- isWeekend: Whether the day is a weekend based on employee work schedule\n- workDayLength: Expected work hours for the day\n- mood: Attendance mood (if attendance enabled)\n- timeFrom/timeTo: Clock in/out times (if attendance enabled)\n- totalHours: Total hours worked (attendance) or logged (time booking)\n- spentHours: Hours logged on tasks (requires Tasks.seeLoggedTime permission)\n- chargeableTime: Hours logged on billable projects (requires Tasks.seeLoggedTime permission)\n- chargeableGoal: Daily chargeable hours goal for the employee\n- holiday: Name of public holiday if applicable\n- vacation: Vacation request data if day falls within a vacation period\n- sickness: Sickness record data if day falls within a sickness period\n- contractStarted: Whether employee contract has started by this date\n- comment: Optional comment for the day\n- highlight: Whether the day should be highlighted in UI","operationId":"TimetableController_getTimeTable","parameters":[{"name":"dateStart","required":true,"in":"query","description":"Start date of the time table range in ISO 8601 format (YYYY-MM-DD). The date is normalized to UTC at midnight. If dateStart is greater than dateEnd, they will be automatically swapped.","schema":{"example":"2025-10-06","type":"string"}},{"name":"dateEnd","required":true,"in":"query","description":"End date of the time table range in ISO 8601 format (YYYY-MM-DD). The date is normalized to UTC at midnight. All days from dateStart to dateEnd (inclusive) are included in the response.","schema":{"example":"2025-10-26","type":"string"}},{"name":"usersFilter","required":true,"in":"query","description":"Filter to control which employees are included in the response. Options: \"all\" (requires TimeTablePermissions.viewAll), \"me\" (requires TimeTablePermissions.viewOwn or viewTeams), or \"team:{teamId}\" (requires TimeTablePermissions.viewTeams and user must be in the team). The team ID format must be exactly \"team:\" followed by the team UUID with no spaces.","schema":{"example":"me","type":"string"}}],"responses":{"200":{"description":"Time table data successfully retrieved. Returns an object keyed by userId.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/TimeTableUserRowDto"}}}}},"400":{"description":"Invalid date format or filter value"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get time table data for date range","tags":["time","timetable"],"x-required-scopes":["api:read"]}},"/account/time-tracker":{"get":{"description":"Returns all time trackers for the current user. The Time Tracker is a real-time time tracking feature that allows users to measure work time while actively working on tasks or projects.\n\nEach tracker can be associated with a specific task or project, includes activity categorization, optional comments, and tracks time through intervals (start/end periods).\n\nResponse includes calculated fields:\n- isRunning: Whether the tracker currently has an active interval\n- hours: Total tracked time in hours (sum of all intervals plus correction)\n- autoEnded: Whether any interval was automatically ended at end of day\n- isOld: Whether the tracker contains intervals from previous days\n- hoursPadded/minutesPadded: Formatted time strings for display\n\nAlso returns lastInteraction timestamp indicating when the user last interacted with any tracker.","operationId":"TimeTrackerController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackerListResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all time trackers","tags":["account","time-tracker"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new time tracker and starts real-time tracking. When a new tracker is created, it automatically stops all other running trackers for the user (only one tracker can run at a time).\n\nThe tracker immediately starts with an active interval beginning at the current time. You can specify:\n- timeTrackingType: Whether to track on Task or Project level\n- projectId: Optional project to associate with\n- taskId: Optional task to associate with (requires timeTrackingType: Task)\n- activityId: Optional activity category\n- comment: Optional description of the work\n- correction: Optional time adjustment in minutes (can be positive or negative)\n\nReturns the created tracker with calculated fields including isRunning: true.","operationId":"TimeTrackerController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerCreateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerResponseDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create new time tracker","tags":["account","time-tracker"],"x-required-scopes":["api:write"]}},"/account/time-tracker/{id}":{"get":{"description":"Returns a specific time tracker by ID with all calculated fields. The tracker must belong to the authenticated user.\n\nCalculated fields include running status, total hours (from intervals and correction), formatted time strings, and flags for auto-ended intervals or old trackers.","operationId":"TimeTrackerController_getOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tracker not found or does not belong to user"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get single time tracker","tags":["account","time-tracker"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates metadata of an existing time tracker. You can modify any of the following fields:\n- timeTrackingType: Change between Task and Project tracking\n- projectId: Change or remove project association\n- taskId: Change or remove task association\n- activityId: Change or remove activity category\n- comment: Update or remove the comment\n- correction: Adjust tracked time in minutes (positive to add time, negative to subtract)\n\nAll fields are optional - only provided fields will be updated. The tracker must belong to the authenticated user.\n\nNote: This does not affect the tracker intervals or running status. Use pause/resume endpoints to control time tracking.","operationId":"TimeTrackerController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerUpdateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerResponseDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tracker not found or does not belong to user"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update existing time tracker","tags":["account","time-tracker"],"x-required-scopes":["api:write"]},"delete":{"description":"Permanently deletes a time tracker and all its associated intervals. This action cannot be undone.\n\nOnly the tracker owner can delete their own trackers. If the tracker is currently running, it will be stopped before deletion.","operationId":"TimeTrackerController__delete","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tracker not found or does not belong to user"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete time tracker","tags":["account","time-tracker"],"x-required-scopes":["api:write"]}},"/account/time-tracker/{id}/pause":{"post":{"description":"Pauses a running time tracker by ending its current active interval. The interval end time is set to the current time.\n\nAfter pausing, the tracker stops accumulating time but can be resumed later using the resume endpoint. This is useful for breaks or when switching to other work.\n\nIf the tracker is not currently running, this endpoint has no effect.","operationId":"TimeTrackerController_pause","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tracker not found or does not belong to user"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Pause running time tracker","tags":["account","time-tracker"],"x-required-scopes":["api:write"]}},"/account/time-tracker/{id}/resume":{"post":{"description":"Resumes a paused time tracker by creating a new active interval starting at the current time.\n\nWhen resuming a tracker, all other running trackers for the user are automatically paused (only one tracker can run at a time). This ensures accurate time tracking without overlaps.\n\nThe tracker will continue accumulating time until paused or stopped again.","operationId":"TimeTrackerController_resume","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeTrackerResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Tracker not found or does not belong to user"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Resume paused time tracker","tags":["account","time-tracker"],"x-required-scopes":["api:write"]}},"/administration/company-settings":{"get":{"description":"Retrieves the company master data including legal entity information, contact details, and address.\n\n**What is Company Data?**\nCompany data contains all the legal, administrative, and contact details of your company. This information is used automatically throughout the system in invoices, contracts, projects, and other documents.\n\n**What is returned:**\n- **Company identification**: Official company name and legal form (e.g., GmbH, AG, LLC)\n- **Complete address**: Country, postal code, city, street, house number\n- **Contact information**: Phone number, email address, website URL, fax number\n- **Tax and registry details**: Tax identification number, business registration number, registration court\n- **Company description**: Short description of what the company does\n- **Financial information**: Bank account number (IBAN) used for invoices\n\n**Note:** This endpoint requires the `Company.view` permission. Company data serves as the foundation for correct legal information, consistent communication, and automated document generation throughout Leadtime.","operationId":"CompanySettingsController_getCompanySettings","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCompanySettingsDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get company settings","tags":["administration","company-settings"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates company master data. All fields are optional - only provided fields will be updated.\n\n**What is Company Data?**\nCompany data contains all the legal, administrative, and contact details of your company. This information is used automatically throughout the system in invoices, contracts, projects, and other documents.\n\n**What can be updated:**\n- **Company identification**: Official company name and legal form (e.g., GmbH, AG, LLC, sole proprietorship)\n- **Complete address**: Country, postal code, city, street, house number\n- **Contact information**: Phone number, email address, website URL, fax number\n- **Tax and registry details**: Tax identification number (needed for invoices), business registration number, registration court\n- **Company description**: Short description of what the company does (e.g., \"Manufacturer of Leadtime – an ERP system for software providers\")\n- **Financial information**: Bank account number (IBAN format) used for invoices\n\n**Validation rules:**\n- Email addresses must be valid email format\n- Website URLs must be valid URL format\n- All string fields can be empty strings to clear values\n\n**Note:** This endpoint requires the `Company.manage` permission. Changes take effect immediately and are used throughout the system for document generation and communication.","operationId":"CompanySettingsController_patchCompanySettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchCompanySettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update company settings","tags":["administration","company-settings"],"x-required-scopes":["api:write"]}},"/organizations/members/journal":{"get":{"description":"Retrieves a paginated grid of organization member journal entries with filtering and sorting capabilities. Filterable fields: id, memberId, createdAt, createdBy, mood, reminder. Sortable fields: id, memberId, createdAt, createdBy, mood, reminder.\n\n**What are Organization Member Journals?**\nOrganization member journals are used to document notes, observations, conversations, and feedback about external organization members (contacts). They help track interactions, agreements, and important information about business partners, customers, suppliers, or other external contacts.\n\n**What is returned:**\n- Journal entry ID and associated member ID\n- Rich text content (body) converted to HTML format\n- Mood indicator (Happy, Neutral, or Sad)\n- Optional reminder date for follow-ups\n- Creation and update timestamps\n- Creator user ID\n\n**Permission-based filtering:**\n- Users with `Organizations.seeJournal` permission: See all journal entries for all members\n- Users without `Organizations.seeJournal` permission: Only see entries for their own member record where `visibleToSubject = true`\n- Organization members can create their own entries, which are automatically marked as visible to themselves\n\n**Filtering options:**\n- Filter by `memberId` to view entries for a specific organization member\n- Filter by `mood` to find entries with specific emotional indicators\n- Filter by `createdAt` or `lastUpdated` to find entries within date ranges\n- Filter by `reminder` to find entries with upcoming or past reminders\n\n**Use cases:**\n- Track conversations and agreements with external contacts\n- Document feedback and observations about business partners\n- Set reminders for follow-up actions\n- Maintain a chronological record of interactions with organization members","operationId":"OrganizationMembersJournalController_listMemberJournals","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Journal entry ID\n- **memberId** (string): Organization member ID\n- **createdAt** (date): Journal entry creation timestamp\n- **createdBy** (string): Creator user ID\n- **mood** (set): Journal entry mood\n- **reminder** (date): Reminder date\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **memberId** (string): Organization member ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Journal entry ID\n- **memberId**: Organization member ID\n- **createdAt**: Journal entry creation timestamp\n- **createdBy**: Creator user ID\n- **mood**: Journal entry mood\n- **reminder**: Reminder date\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, memberId, createdAt, createdBy, mood, reminder, body","schema":{"example":"id,memberId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of organization member journal entries"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List organization member journal entries","tags":["organizations","organization-members","journal","organizations","organization-members"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new journal entry for an organization member (external contact).\n\n**What you need to provide:**\n- `memberId` (required): UUID of the organization member this entry is about\n- `body` (required): Rich text content in HTML or Markdown format. Supports:\n  - Headings, paragraphs, lists, and formatting\n  - Links and embedded content\n  - The content is automatically converted to the internal document format\n- `mood` (optional): Emotional indicator - Happy, Neutral, or Sad (defaults to Neutral)\n- `reminder` (optional): ISO 8601 date string for follow-up reminders (e.g., \"2024-12-31\")\n\n**Access control:**\n- Users with `Organizations.writeJournal` permission can create entries for any member\n- Organization members can create entries for their own member record\n- The `memberId` must exist and be accessible to the current user\n- Returns 400 if the member does not exist or is not accessible\n\n**Visibility behavior:**\n- If the user is creating an entry for their own member record, `visibleToSubject` is automatically set to `true`\n- If the user has `Organizations.writeJournal` permission and creates an entry for another member, `visibleToSubject` is set to `false` (internal only)\n\n**What is returned:**\nThe complete journal entry with all fields, including the body converted to HTML format and timestamps.","operationId":"OrganizationMembersJournalController_createMemberJournal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationMemberJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberJournalResponseDto"}}}},"400":{"description":"Validation errors or member not accessible","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"memberId":["Member ID is required","Invalid memberId or member not accessible"],"body":["Body content is required"],"mood":["Mood must be one of: Happy, Neutral, Sad"],"reminder":["Reminder must be a valid ISO 8601 date string"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create organization member journal entry","tags":["organizations","organization-members","journal","organizations","organization-members"],"x-required-scopes":["api:write"]}},"/organizations/members/journal/{id}":{"get":{"description":"Returns complete details for a specific organization member journal entry.\n\n**What is returned:**\n- Journal entry ID and associated member ID\n- Rich text content (body) converted to HTML format from the internal document format\n- Mood indicator (Happy, Neutral, or Sad)\n- Optional reminder date in ISO 8601 format (null if not set)\n- Creation timestamp (when the entry was created)\n- Last update timestamp (when the entry was last modified)\n- Creator user ID (who created the entry)\n\n**Access control:**\n- Creator of the entry can access\n- Users with `Organizations.writeJournal` permission can access any journal entry\n- Returns 404 if the entry does not exist, has been deleted, or the user does not have access\n\n**Note:** The body content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications.","operationId":"OrganizationMembersJournalController_getMemberJournalDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberJournalResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization member journal entry details","tags":["organizations","organization-members","journal","organizations","organization-members"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates specific fields of an existing organization member journal entry. Only provided fields are updated; all fields are optional.\n\n**What you can update:**\n- `memberId` (optional): Change which member this entry is associated with\n- `body` (optional): Update the rich text content (HTML or Markdown format)\n- `mood` (optional): Change the emotional indicator (Happy, Neutral, or Sad)\n- `reminder` (optional): Update or clear the reminder date\n  - Provide an ISO 8601 date string (e.g., \"2024-12-31\") to set/update\n  - Provide `null` to clear an existing reminder\n  - Omit the field to keep the existing reminder unchanged\n\n**Access control:**\n- Users with `Organizations.writeJournal` permission can update any journal entry\n- Organization members can update their own entries where `visibleToSubject = true`\n- If updating `memberId`, the new member must exist and be accessible\n- Returns 404 if the entry does not exist, has been deleted, or the user does not have access\n- Returns 400 if the new `memberId` is invalid or not accessible\n\n**Visibility behavior:**\n- If changing `memberId` to the user's own member record, `visibleToSubject` is automatically set to `true`\n- Otherwise, the existing `visibleToSubject` value is preserved\n\n**What is returned:**\nThe complete updated journal entry with all fields, including the body converted to HTML format and updated timestamps.","operationId":"OrganizationMembersJournalController_patchMemberJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOrganizationMemberJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberJournalResponseDto"}}}},"400":{"description":"Validation errors or member not accessible"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update organization member journal entry","tags":["organizations","organization-members","journal","organizations","organization-members"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an organization member journal entry. The entry is marked as deleted but not permanently removed from the database.\n\n**What happens:**\n- The journal entry is marked with a `deletedAt` timestamp\n- The entry will no longer appear in list queries or be accessible via the API\n- The data is preserved in the database for audit purposes\n- The entry cannot be restored through the API (restoration would require database access)\n\n**Access control:**\n- Users with `Organizations.writeJournal` permission can delete any journal entry\n- Organization members can delete their own entries where `visibleToSubject = true`\n- Returns 404 if the entry does not exist, has already been deleted, or the user does not have access\n\n**Note:** This is a soft delete operation. The entry is not permanently removed and can potentially be recovered by database administrators if needed.","operationId":"OrganizationMembersJournalController_deleteMemberJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete organization member journal entry","tags":["organizations","organization-members","journal","organizations","organization-members"],"x-required-scopes":["api:write"]}},"/organizations/members":{"get":{"description":"**Organization Members** are external contacts associated with organizations (customers, partners, suppliers, etc.). They represent people you work with outside your company.\n\nThis endpoint returns a paginated, filterable, and sortable list of all organization members in your workspace.\n\n**What you can do:**\n- Filter members by organization, status, position, or role\n- Search by name or email\n- Sort by any column\n- Select specific fields to return\n\n**Returns:** A grid response with organization member data including name, position, email, active status, login capability, and avatar information.\n\nRetrieves a paginated grid of organization members with filtering and sorting capabilities. Quick search available on: firstName, lastName, email. Filterable fields: id, organizationId, firstName, lastName, email, position, isActive, canLogin, gender, phone, addressCity, addressCountry, roleId, userId, avatarId, createdAt, editedAt. Sortable fields: id, organizationId, firstName, lastName, email, position, isActive, canLogin, gender, addressCity, addressCountry, roleId, userId, createdAt, editedAt.","operationId":"OrganizationMembersController_listOrganizationMembers","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: firstName, lastName, email","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Organization member ID\n- **organizationId** (string): Organization ID\n- **firstName** (string): First name\n- **lastName** (string): Last name\n- **email** (string): Email address\n- **position** (string): Position/title in organization\n- **isActive** (boolean): Whether member is active\n- **canLogin** (boolean): Whether member can login (derived from user status)\n- **gender** (set): Gender\n- **phone** (string): Phone number\n- **addressCity** (string): City\n- **addressCountry** (string): Country\n- **roleId** (string): User role ID\n- **userId** (string): Associated user ID\n- **avatarId** (string): Avatar file ID\n- **createdAt** (date): Creation timestamp\n- **editedAt** (date): Last update timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Organization member ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **firstName** (string): First name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **lastName** (string): Last name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **position** (string): Position/title in organization (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **isActive** (boolean): Whether member is active (boolean filter). Available comparisons: equal, not_equal\n- **canLogin** (boolean): Whether member can login (derived from user status) (boolean filter). Available comparisons: equal, not_equal\n- **gender** (set): Gender (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **phone** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **roleId** (string): User role ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **userId** (string): Associated user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **avatarId** (string): Avatar file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Organization member ID\n- **organizationId**: Organization ID\n- **firstName**: First name\n- **lastName**: Last name\n- **email**: Email address\n- **position**: Position/title in organization\n- **isActive**: Whether member is active\n- **canLogin**: Whether member can login (derived from user status)\n- **gender**: Gender\n- **addressCity**: City\n- **addressCountry**: Country\n- **roleId**: User role ID\n- **userId**: Associated user ID\n- **createdAt**: Creation timestamp\n- **editedAt**: Last update timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** lastName (asc)","schema":{"example":"[{\"field\":\"lastName\",\"direction\":\"asc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, organizationId, firstName, lastName, email, position, isActive, canLogin, gender, phone, addressCity, addressCountry, roleId, userId, avatarId, createdAt, editedAt","schema":{"example":"id,organizationId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of organization members"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List organization members","tags":["organizations","organization-members"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new organization member (external contact) for an organization. This automatically creates a user account that can be used for guest access to Leadtime.\n\n**What happens:**\n1. A new organization member record is created\n2. A user account is automatically created and linked to the member\n3. If `canLogin` is `true`:\n   - User status is set to Active\n   - An invitation email is sent with a magic link\n   - The user can log in as a guest user\n4. If `canLogin` is `false` or omitted:\n   - User status is set to Inactive\n   - No invitation is sent\n   - The member exists as a contact only\n\n**Required fields:**\n- `organizationId` - The organization this member belongs to\n- `firstName` - Member's first name\n- `lastName` - Member's last name\n- `email` - Email address (used for user account)\n- `roleId` - User role ID (e.g., `workspace-id_guest_` for guest access)\n\n**Avatar upload process:**\n1. First, call `POST /workspace/upload` to upload the image file\n2. Receive a file ID in the response\n3. Use that file ID in the `avatarId` field when creating the member\n\n**Personality classifications:**\n- **Influence level:** Low, Medium, High, Dominant\n- **Attitude level:** Enthusiastic, WellMeaning, Neutral, Opposing\n- **Personality type:** Dominant, Conscientious, Steady, Initiative\n\n**Returns:** The created organization member with all details including the generated user ID.","operationId":"OrganizationMembersController_createOrganizationMember","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationMemberDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"firstName":["First name is required"],"lastName":["Last name is required"],"email":["Email is required","Email must be valid"],"roleId":["Role ID is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create organization member","tags":["organizations","organization-members"],"x-required-scopes":["api:write"]}},"/organizations/members/{id}":{"get":{"description":"Returns complete details for a specific organization member.\n\n**What's included:**\n- **Basic information:** Name, position, academic degree, date of birth, gender, active status\n- **Access to Leadtime:** Whether the member can log in, their role ID, and associated user ID\n- **Contact details:** Full address (street, city, ZIP, country, house number), phone, email, social network links\n- **Personality traits:** Influence level, attitude level, and personality type classifications\n- **Avatar:** Profile picture URL if available\n- **Timestamps:** Creation and last update dates\n\n**Use cases:**\n- View complete member profile\n- Check access permissions and login status\n- Retrieve contact information for communication\n- Review personality classifications for relationship management","operationId":"OrganizationMembersController_getOrganizationMemberDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization member details","tags":["organizations","organization-members"],"x-required-scopes":["api:read"]},"patch":{"description":"Partially updates an existing organization member. Only the fields you provide will be updated; all other fields remain unchanged.\n\n**Automatic user synchronization:**\nChanges to certain fields automatically sync to the associated user account:\n- `firstName` → Updates user's first name\n- `lastName` → Updates user's last name\n- `email` → Updates user's email address\n- `roleId` → Updates user's role\n- `canLogin` → Controls user access and status\n\n**Login access changes:**\n- **Enabling login** (`canLogin: false → true`):\n  - User status changes to Active\n  - An invitation email is sent with a magic link\n  - User can now log in as a guest user\n- **Disabling login** (`canLogin: true → false`):\n  - User status changes to Inactive\n  - Invite token is cleared\n  - User can no longer log in\n\n**Avatar management:**\n- To update avatar: Upload via `POST /workspace/upload` first, then provide the file ID\n- To remove avatar: Set `avatarId` to `null` or an empty string\n\n**All other fields** (address, phone, personality traits, etc.) can be updated independently without affecting the user account.","operationId":"OrganizationMembersController_updateOrganizationMember","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOrganizationMemberDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization member","tags":["organizations","organization-members"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an organization member and the associated user account.\n\n**What happens:**\n- The organization member is marked as deleted (soft delete)\n- The associated user account is also marked as deleted\n- The avatar file is permanently deleted from storage\n- Records remain in the database but are hidden from normal queries\n\n**Note:** This is a soft delete operation. The data is not permanently removed and could potentially be restored through database operations if needed.","operationId":"OrganizationMembersController_deleteOrganizationMember","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete organization member","tags":["organizations","organization-members"],"x-required-scopes":["api:write"]}},"/organizations/letters":{"get":{"description":"**What are Organization Letters?**\nOrganization letters are formal business correspondence documents that can be created, managed, and stored directly in Leadtime. They provide centralized, traceable communication linked to each organization, reducing media discontinuity and making it easier to document business correspondence.\n\n**What this endpoint does:**\nReturns a paginated, sortable, and filterable list of all organization letters in the workspace. You can view letters for a specific organization or all letters across the workspace.\n\n**What data is returned:**\n- Letter metadata: ID, subject, date, organization, member (if applicable)\n- Timestamps: creation date, last update date\n- Creator information: who created the letter\n- Address type: whether the letter is addressed to the organization or a specific member\n\n**Supported operations:**\nRetrieves a paginated grid of organization letters with filtering and sorting capabilities. Quick search available on: subject. Filterable fields: id, organizationId, organizationName, memberId, subject, dateOfLetter, addressType, createdAt, lastUpdated, createdBy. Sortable fields: id, organizationId, organizationName, memberId, subject, dateOfLetter, addressType, createdAt, lastUpdated, createdBy.\n\n**Use cases:**\n- View all letters for a specific organization\n- Search and filter letters by subject, date, or member\n- Track letter creation and modification history\n- Export letter lists for reporting\n\n**Note:** Requires `seeLetters` permission on the Organizations resource.","operationId":"OrganizationLettersController_listOrganizationLetters","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: subject","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Letter ID\n- **organizationId** (set): Organization ID\n- **organizationName** (string): Organization name\n- **memberId** (set): Organization member ID (null if not member-specific)\n- **subject** (string): Letter subject\n- **dateOfLetter** (date): Date of the letter\n- **addressType** (set): Address type (Organization or Member)\n- **createdAt** (date): Creation timestamp\n- **lastUpdated** (date): Last update timestamp\n- **createdBy** (set): ID of user who created the letter\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Letter ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **organizationId** (set): Organization ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **organizationName** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **memberId** (set): Organization member ID (null if not member-specific) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **subject** (string): Letter subject (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **dateOfLetter** (date): Date of the letter (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **addressType** (set): Address type (Organization or Member) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **lastUpdated** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (set): ID of user who created the letter (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Letter ID\n- **organizationId**: Organization ID\n- **organizationName**: Organization name\n- **memberId**: Organization member ID (null if not member-specific)\n- **subject**: Letter subject\n- **dateOfLetter**: Date of the letter\n- **addressType**: Address type (Organization or Member)\n- **createdAt**: Creation timestamp\n- **lastUpdated**: Last update timestamp\n- **createdBy**: ID of user who created the letter\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** lastUpdated (desc)","schema":{"example":"[{\"field\":\"lastUpdated\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, organizationId, organizationName, memberId, subject, dateOfLetter, addressType, createdAt, lastUpdated, createdBy","schema":{"example":"id,organizationId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of organization letters"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List organization letters","tags":["organizations","organization-letters","organizations"],"x-required-scopes":["api:read"]},"post":{"description":"**What this endpoint does:**\nCreates a new formal business letter linked to an organization. Letters can be addressed to the organization itself or to a specific organization member (contact person).\n\n**Required information:**\n- **Organization ID**: The organization this letter belongs to (must exist in workspace)\n- **Subject**: Title or topic of the letter\n- **Date of letter**: The date shown on the letter (can be backdated if needed)\n- **Address type**: Choose between organization address or member's private address\n- **Body content**: The letter text in HTML or Markdown format\n\n**Optional information:**\n- **Member ID**: If provided, links the letter to a specific organization member/contact person. The member must belong to the specified organization.\n\n**Content formatting:**\nAccepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your organization letter body. Variables are represented as HTML spans:\n\n`clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`, `<span data-type=\"variable\" data-id=\"contactPerson\">John Doe</span>`\n\n**Validation rules:**\n- Organization ID must exist and be accessible in your workspace\n- If member ID is provided, it must belong to the specified organization\n- Subject cannot be empty\n- Body content cannot be empty\n- Date must be a valid date\n\n**What happens after creation:**\n- Letter is saved and linked to the organization\n- Letter appears in the organization's letters list\n- Letter can be viewed, edited, or downloaded as PDF\n- Letter body is automatically converted from HTML/Markdown to internal format (IDoc)\n\n**Use cases:**\n- Create formal correspondence with customers or partners\n- Generate personalized letters using template variables\n- Document business communication in a centralized location\n- Track all letters sent to specific organizations or members\n\n**Note:** Requires `writeLetters` permission on the Organizations resource.","operationId":"OrganizationLettersController_createOrganizationLetter","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLetterDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LetterResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"organizationId":["Organization ID is required"],"subject":["Subject is required"],"dateOfLetter":["Date is required"],"addressType":["Address type is required"],"body":["Body content is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create organization letter","tags":["organizations","organization-letters","organizations"],"x-required-scopes":["api:write"]}},"/organizations/letters/{id}":{"get":{"description":"**What this endpoint does:**\nRetrieves complete details of a specific organization letter, including the full letter body content converted to HTML format.\n\n**What is returned:**\n- Letter metadata: ID, subject, date, organization ID, member ID (if applicable)\n- Address type: Organization or Member\n- Body content: Full letter text in HTML format (converted from internal IDoc format)\n- Timestamps: Creation date and last update date\n- Creator: User ID of who created the letter\n\n**Use cases:**\n- Display letter content in a viewer or editor\n- Generate PDF exports of letters\n- Retrieve letter details for editing\n- Audit letter history and metadata\n\n**Note:** Requires `seeLetters` permission on the Organizations resource.","operationId":"OrganizationLettersController_getOrganizationLetterDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LetterResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization letter details","tags":["organizations","organization-letters","organizations"],"x-required-scopes":["api:read"]},"patch":{"description":"**What this endpoint does:**\nUpdates specific fields of an existing organization letter. Only the fields you provide will be updated; all other fields remain unchanged. This allows you to make targeted changes without resubmitting the entire letter.\n\n**What can be updated:**\n- **Organization ID**: Change which organization the letter belongs to\n- **Member ID**: Link/unlink the letter to a specific member (use `null` to clear the member association)\n- **Subject**: Update the letter title\n- **Date of letter**: Change the date shown on the letter\n- **Address type**: Switch between organization address and member address\n- **Body content**: Update the letter text\n\n**Content formatting:**\nAccepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your organization letter body. Variables are represented as HTML spans:\n\n`clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`, `<span data-type=\"variable\" data-id=\"contactPerson\">John Doe</span>`\n\n**Validation rules:**\n- Letter must exist and be accessible in your workspace\n- If organization ID is provided, it must exist and be accessible\n- If member ID is provided (or set to `null`), the member must belong to the specified organization (or the letter's current organization if organization ID is not being updated)\n- All field validations from the create endpoint apply to updated fields\n\n**How partial updates work:**\n- Fields not provided in the request remain unchanged\n- If you provide `memberId: null`, it clears the member association\n- Body content is automatically converted from HTML/Markdown to internal format (IDoc) if provided\n- The `lastUpdated` timestamp is automatically updated\n\n**Use cases:**\n- Correct typos or update letter content\n- Change the recipient (member) of an existing letter\n- Update letter date if backdating is needed\n- Modify letter subject or address type\n\n**Note:** Requires `writeLetters` permission on the Organizations resource.","operationId":"OrganizationLettersController_patchOrganizationLetter","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchLetterDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LetterResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update organization letter","tags":["organizations","organization-letters","organizations"],"x-required-scopes":["api:write"]},"delete":{"description":"**What this endpoint does:**\nSoft deletes an organization letter, marking it as deleted without permanently removing it from the database. The letter will no longer appear in normal listings but can be recovered if needed.\n\n**What happens:**\n- Letter is marked as deleted (soft delete)\n- Letter disappears from organization letters list\n- Letter data is preserved in the database\n- Letter can potentially be restored through database operations\n\n**Use cases:**\n- Remove letters that are no longer needed\n- Clean up test or draft letters\n- Maintain data integrity while allowing cleanup\n\n**Note:** Requires `writeLetters` permission on the Organizations resource.","operationId":"OrganizationLettersController_deleteOrganizationLetter","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete organization letter","tags":["organizations","organization-letters","organizations"],"x-required-scopes":["api:write"]}},"/administration/organization-settings/custom-fields":{"get":{"description":"Retrieves all custom fields configured for organizations in the workspace.\n\n**What are Custom Fields?**\nCustom fields allow you to capture additional information about organizations that matters to your company. These fields are flexible and can hold different types of data.\n\n**Supported field types:**\n- Text: Single-line text input\n- Textarea: Multi-line text input\n- Number: Numeric values\n- Date: Date selection\n- Checkbox: Boolean true/false\n- Select: Single selection from predefined options\n- MultiSelect: Multiple selections from predefined options\n\n**Common use cases:**\n- Industry classification (e.g., Technology, Healthcare, Finance)\n- Business relationship data (e.g., Contract type, Payment terms)\n- CRM-relevant information (e.g., Lead source, Account tier)\n- Individual extra info for special organization types\n\n**What is returned:**\n- Field ID, name, description, type, and entity (always \"Organization\")\n- Sort order (for display ordering)\n- Multilingual translations\n- Select options (for Select and MultiSelect types)\n\n**Note:** Custom fields are especially useful in CRM and add organization management features with specific data points tailored to your business needs.","operationId":"OrganizationSettingsController_getOrganizationCustomFields","parameters":[],"responses":{"200":{"description":"Organization custom fields retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List organization custom fields","tags":["administration","organization-settings","organization-custom-fields"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new custom field for capturing additional organization information.\n\n**Required fields:**\n- `name`: Display name for the field (e.g., \"Priority Level\", \"Region\")\n- `type`: Field type enum value (Text, Textarea, Number, Date, Checkbox, Select, MultiSelect)\n- `translations`: Array of translations for multilingual support\n\n**Optional fields:**\n- `id`: Optional ID for the field (for updating existing fields)\n- `description`: Additional context about the field\n- `selectOptions`: Required for Select and MultiSelect types - array of option objects with:\n  - `id`: Optional identifier (use value if not provided)\n  - `value`: Display value for the option\n  - `translations`: Array of translations for the option name\n- `translationsDescriptions`: Translations for the field description\n\n**Field type requirements:**\n- Select and MultiSelect types MUST include selectOptions array\n- Other types do not use selectOptions\n\n**Note:** Once created, the custom field will appear when creating or editing organizations. The field can be reordered using the sort endpoint.","operationId":"OrganizationSettingsController_createOrganizationCustomField","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationCustomFieldDto"}}}},"responses":{"201":{"description":"Organization custom field created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create organization custom field","tags":["administration","organization-settings","organization-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/organization-settings/custom-fields/{id}":{"put":{"description":"Updates an existing organization custom field. All fields must be provided (full replacement).\n\n**Required fields:**\n- `name`: Updated display name\n- `type`: Field type enum value\n- `translations`: Complete translations array (all languages)\n\n**Optional fields:**\n- `description`: Updated description\n- `selectOptions`: Updated options array (required for Select and MultiSelect types)\n- `translationsDescriptions`: Updated description translations\n\n**Important:**\n- Changing field type may cause data loss if existing values are incompatible\n- For Select/MultiSelect types, all options must be included in the request\n- Removing options that are in use may cause validation errors\n\n**Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields.","operationId":"OrganizationSettingsController_updateOrganizationCustomField","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationCustomFieldDto"}}}},"responses":{"200":{"description":"Organization custom field updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationCustomFieldResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization custom field","tags":["administration","organization-settings","organization-custom-fields"],"x-required-scopes":["api:write"]},"delete":{"description":"Deletes an organization custom field using soft delete (marked as deleted but not permanently removed).\n\n**Important considerations:**\n- The field will no longer appear in organization forms\n- Existing organization data stored in this field will be retained but not displayed\n- Deleted fields can be restored using the restore defaults endpoint\n\n**Note:** This operation requires workspace settings management permission. Consider exporting data before deleting fields if you need to preserve historical information.","operationId":"OrganizationSettingsController_deleteOrganizationCustomField","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Organization custom field deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete organization custom field","tags":["administration","organization-settings","organization-custom-fields"],"x-required-scopes":["api:write"]}},"/administration/organization-settings/custom-fields/sort":{"post":{"description":"Updates the display order of organization custom fields by providing an ordered array of field IDs.\n\n**How it works:**\n1. Provide an array of custom field IDs in the desired display order\n2. The first ID in the array will have sort order 0, second will have 1, etc.\n3. Fields not included in the array will maintain their current sort order\n\n**Example:**\nIf you provide [\"field-3\", \"field-1\", \"field-2\"], then:\n- field-3 will appear first in organization forms\n- field-1 will appear second\n- field-2 will appear third\n\n**Note:** This affects the order in which custom fields appear in organization creation and editing forms.","operationId":"OrganizationSettingsController_sortOrganizationCustomFields","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortOrganizationCustomFieldsDto"}}}},"responses":{"200":{"description":"Organization custom fields reordered successfully"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Reorder organization custom fields","tags":["administration","organization-settings","organization-custom-fields"],"x-required-scopes":["api:write"]}},"/organizations":{"get":{"description":"Returns a paginated, filterable, and sortable grid of all organizations in the workspace.\n\n**What are Organizations?**\nOrganizations represent external business partners that your company has contact with:\n- **Customers**: Existing business partners with ongoing projects\n- **Prospects**: Potential customers with first contact or active offers\n- **Suppliers**: External providers of goods or services\n- **Partners**: Strategic partners or resellers\n- **Competitors**: Competitors in the same industry\n- **Service Providers**: Supporting external providers (e.g., for support)\n- **Investors**: Financial or institutional investors\n- **Authorities**: Public institutions or government agencies\n- **Educational Institutions**: Universities, schools, or research institutes\n- **Target Groups**: Companies that belong to your target customer group\n\nOrganizations are the foundation for managing external projects, contacts (organization members), invoices, and business relationships. They allow you to structure and analyze all external partnerships in one place.\n\n**What is returned:**\n- Organization identification (ID, name, short name, type, color, icon)\n- Status information (active/inactive)\n- Relationship data (number of linked projects, number of members/contacts)\n- Creation and update timestamps\n- Logo information\n\n**Filtering and search:**\n- Quick search across organization name, short name, and other fields\n- Advanced filtering by type, status, and other attributes\n- Sorting by any sortable field\n- Server-side pagination for large organization lists\n\n**Note:** This endpoint requires the Organizations.view permission. Retrieves a paginated grid of organizations with filtering and sorting capabilities. Quick search available on: name, shortName. Filterable fields: id, name, shortName, type, isActive, email, phoneNumber, website, addressCity, addressCountry, addressStreet, addressZip, startOfCollaboration, createdAt, editedAt, isFavorite, parentOrganizationId, parentOrganizationName. Sortable fields: id, name, shortName, type, isActive, addressCity, addressCountry, startOfCollaboration, createdAt, editedAt, isFavorite, parentOrganizationId.","operationId":"OrganizationsController_listOrganizations","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: name, shortName","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Organization ID\n- **name** (string): Organization name\n- **shortName** (string): Short identifier\n- **type** (set): Organization type (Customer, Vendor, Partner, etc.)\n- **isActive** (boolean): Whether organization is active\n- **email** (string): Email address\n- **phoneNumber** (string): Phone number\n- **website** (string): Website URL\n- **addressCity** (string): City\n- **addressCountry** (string): Country\n- **addressStreet** (string): Street address\n- **addressZip** (string): ZIP/Postal code\n- **startOfCollaboration** (date): Start of collaboration date\n- **createdAt** (date): Creation timestamp\n- **editedAt** (date): Last update timestamp\n- **isFavorite** (boolean): Whether the organization is marked as favorite by the current user\n- **parentOrganizationId** (string): Parent organization ID\n- **parentOrganizationName** (string): Parent organization name\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **name** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **shortName** (string): Short identifier (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **type** (set): Organization type (Customer, Vendor, Partner, etc.) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **isActive** (boolean): Whether organization is active (boolean filter). Available comparisons: equal, not_equal\n- **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **phoneNumber** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **website** (string): Website URL (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressStreet** (string): Street address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressZip** (string): ZIP/Postal code (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **startOfCollaboration** (date): Start of collaboration date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **isFavorite** (boolean): Whether the organization is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal\n- **parentOrganizationId** (string): Parent organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **parentOrganizationName** (string): Parent organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Organization ID\n- **name**: Organization name\n- **shortName**: Short identifier\n- **type**: Organization type (Customer, Vendor, Partner, etc.)\n- **isActive**: Whether organization is active\n- **addressCity**: City\n- **addressCountry**: Country\n- **startOfCollaboration**: Start of collaboration date\n- **createdAt**: Creation timestamp\n- **editedAt**: Last update timestamp\n- **isFavorite**: Whether the organization is marked as favorite by the current user\n- **parentOrganizationId**: Parent organization ID\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** name (asc)","schema":{"example":"[{\"field\":\"name\",\"direction\":\"asc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, name, shortName, type, color, isActive, email, phoneNumber, website, addressCity, addressCountry, addressStreet, addressZip, startOfCollaboration, createdAt, editedAt, isFavorite, parentOrganizationId, parentOrganizationName","schema":{"example":"id,name","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of organizations"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List organizations","tags":["organizations"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new organization (external business partner) in your workspace.\n\n**What are Organizations?**\nOrganizations represent external business partners like customers, suppliers, partners, or competitors. They serve as the foundation for managing external projects, contacts, invoices, and business relationships.\n\n**Required fields:**\n- name: Full company name (e.g., \"Acme Corporation\")\n- type: Organization type (Customer, Prospect, Supplier, Partner, etc.)\n- color: Hex color code for visual identification (e.g., \"#FF5733\")\n\n**Optional fields:**\n- shortName: 3-5 character abbreviation (auto-generated if not provided)\n- icon: Icon identifier for visual representation\n- legalForm: Legal company form (e.g., \"GmbH\", \"AG\", \"LLC\")\n- startOfCollaboration: Date when business relationship started (ISO date format: YYYY-MM-DD)\n- Contact information: Address fields, phone, fax, email, website\n- Legal information: Tax number, registration number, registration court\n- Invoice settings: Hourly rate, payment terms, reminder fees, interest rates\n- Document settings: Table of contents, title page, heading style\n\n**Uploading a logo:**\n1. First, call POST /api/public/workspace/upload to upload the logo file\n2. The upload endpoint returns a file ID (UUID)\n3. Use that file ID in the logoId field when creating the organization\n\n**Auto-generated fields:**\n- If shortName is not provided, it will be automatically generated from the organization name\n- The organization will be created with isActive: true by default\n\n**What is returned:**\nThe complete organization object with all fields, including computed values like logoUrl and timestamps.\n\n**Note:** This endpoint requires the Organizations.create permission. All validation errors will be returned with detailed field-level messages.","operationId":"OrganizationsController_createOrganization","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"name":["Name is required"],"type":["Type is required"],"color":["Color must be a valid hex code"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create organization","tags":["organizations"],"x-required-scopes":["api:write"]}},"/organizations/{id}/objects":{"get":{"description":"Returns objects whose organization matches this organization. Visibility rules for objects still apply.","operationId":"OrganizationsController_listOrganizationObjects","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":200,"default":50,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedObjectsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List objects for an organization","tags":["organizations"],"x-required-scopes":["api:read"]}},"/organizations/{id}":{"get":{"description":"Retrieves complete details of a specific organization, including all master data, contact information, legal details, and settings.\n\n**What is returned:**\n- **Basic information**: Name, short name, icon, type, color, legal form, description\n- **Contact details**: Full address (street, city, ZIP, country, house number), phone, fax, email, website\n- **Legal information**: Tax number, registration number, registration court\n- **Business relationship**: Start of collaboration date, active status\n- **Invoice settings**: Hourly rate, payment terms, reminder fees, interest rates, invoice language\n- **Document settings**: Table of contents, title page, heading style, default contact\n- **Logo**: Public URL to the organization logo (if uploaded)\n- **Timestamps**: Creation and last update dates\n\n**Note:** This endpoint requires the Organizations.view permission. The organization must exist in your workspace and not be deleted.","operationId":"OrganizationsController_getOrganizationDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization details","tags":["organizations"],"x-required-scopes":["api:read"]},"put":{"description":"Updates an existing organization by replacing all fields. This is a full update operation - all fields must be provided, even if you only want to change a few values.\n\n**When to use:**\nUse this endpoint when you want to update multiple fields or replace the entire organization data. For partial updates (changing only specific fields), use PATCH /organizations/:id instead.\n\n**Required fields:**\nAll fields that are required for creation must be provided:\n- name: Full company name\n- type: Organization type\n- color: Hex color code\n\n**Optional fields:**\nAll other fields can be provided or omitted (will be set to null/undefined if omitted).\n\n**Updating the logo:**\n1. Call POST /api/public/workspace/upload to upload the new logo file\n2. Use the returned file ID in the logoId field\n3. To remove the logo, set logoId to null\n\n**What is returned:**\nThe complete updated organization object with all fields and computed values.\n\n**Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.","operationId":"OrganizationsController_updateOrganization","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization","tags":["organizations"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an organization and all associated projects. The organization and its projects will be marked as deleted but not permanently removed from the database.\n\n**What happens:**\n- The organization is marked as deleted (soft delete)\n- All associated projects are also soft deleted\n- Organization members (contacts) are not deleted\n- Historical data (invoices, journal entries, etc.) remains intact\n- The organization will no longer appear in normal listings\n\n**Recovery:**\nSoft-deleted organizations can potentially be recovered through database operations, but this is not exposed via the API.\n\n**Note:** This endpoint requires the Organizations.delete permission. The organization must exist in your workspace. This operation cannot be undone via the API.","operationId":"OrganizationsController_deleteOrganization","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete organization","tags":["organizations"],"x-required-scopes":["api:write"]},"patch":{"description":"Partially updates an organization. Only the fields you provide will be updated; all other fields remain unchanged. This is the recommended way to update organizations when you only need to change specific fields.\n\n**When to use:**\nUse this endpoint for partial updates (changing only specific fields). For full updates (replacing all fields), use PUT /organizations/:id instead.\n\n**Available fields:**\nAll organization fields can be updated, including:\n- Basic info: name, shortName, icon, type, color, legalForm, description\n- Contact details: address fields, phone, fax, email, website\n- Legal info: taxNumber, registrationNumber, registrationCourt\n- Business: startOfCollaboration, isActive (via full update)\n- Invoice settings: hourRate, invoiceDueDays, reminder fees, interest rates, invoiceLanguage\n- Document settings: table of contents, title page, heading style, default contact\n\n**Updating the logo:**\n1. Call POST /api/public/workspace/upload to upload the new logo file\n2. Use the returned file ID in the logoId field\n3. To remove the logo, set logoId to null\n\n**Behavior:**\n- Omitted fields remain unchanged\n- Provided fields replace existing values\n- Set fields to null to clear them (where allowed)\n- Date fields should be provided in ISO format (YYYY-MM-DD)\n\n**What is returned:**\nThe complete updated organization object with all fields and computed values.\n\n**Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.","operationId":"OrganizationsController_patchOrganization","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOrganizationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update organization","tags":["organizations"],"x-required-scopes":["api:write"]}},"/organizations/{id}/document-settings":{"get":{"description":"Retrieves document formatting settings for a specific organization. These settings control how project documents (quotes, requirement documents, contracts) are formatted for this organization.\n\n**What are Document Settings?**\nDocument settings allow you to customize the appearance and structure of automatically generated project documents on a per-organization basis. These settings override the workspace defaults, enabling customer-specific document layouts and formatting.\n\n**What is returned:**\n- **Organization-specific settings**: Values set for this organization (can be null to inherit from workspace)\n  - projectDocumentEnableToc: Whether to include a table of contents\n  - projectDocumentTitlePage: Whether to show a title/cover page\n  - projectDocumentHeadingStyle: Formatting style for headings (e.g., Sequential, Normal)\n  - projectDocumentDefaultContactUserId: Default contact person for documents\n- **Workspace defaults**: The default values that apply when organization settings are null\n  - projectDocumentEnableTocDefault: Workspace default for table of contents\n  - projectDocumentTitlePageDefault: Workspace default for title page\n  - projectDocumentHeadingStyleDefault: Workspace default heading style\n\n**Inheritance behavior:**\n- If an organization setting is null, the workspace default is used\n- If an organization setting has a value, it overrides the workspace default\n- This allows you to customize documents per customer while maintaining workspace-wide defaults\n\n**Use cases:**\n- Different document layouts for specific customers\n- Custom corporate design requirements\n- Creating documents for external partners without internal formatting\n\n**Note:** This endpoint requires the Organizations.view permission. The organization must exist in your workspace and not be deleted.","operationId":"OrganizationsController_getOrganizationDocumentSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrganizationDocumentSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization document settings","tags":["organizations"],"x-required-scopes":["api:read"]},"patch":{"description":"Partially updates document formatting settings for an organization. Only the fields you provide will be updated; all other fields remain unchanged.\n\n**What are Document Settings?**\nDocument settings control how project documents (quotes, requirement documents, contracts) are formatted for this organization. These settings can override workspace defaults to enable customer-specific layouts.\n\n**Available settings:**\n- projectDocumentEnableToc: Include table of contents (boolean or null to inherit)\n- projectDocumentTitlePage: Show title/cover page (boolean or null to inherit)\n- projectDocumentHeadingStyle: Heading formatting style (enum value or null to inherit)\n- projectDocumentDefaultContactUserId: Default contact person UUID (or null to clear)\n\n**Inheritance:**\n- Set any field to null to inherit the workspace default for that setting\n- Provide a value to override the workspace default for this organization\n- Omit fields you do not want to change (they remain unchanged)\n\n**Example use cases:**\n- Enable table of contents for a specific customer: { \"projectDocumentEnableToc\": true }\n- Reset to workspace default: { \"projectDocumentEnableToc\": null }\n- Set multiple settings at once: { \"projectDocumentEnableToc\": true, \"projectDocumentTitlePage\": false }\n\n**Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.","operationId":"OrganizationsController_updateOrganizationDocumentSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOrganizationDocumentSettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization document settings","tags":["organizations"],"x-required-scopes":["api:write"]}},"/organizations/{id}/custom-fields":{"patch":{"description":"Updates only the custom field values for an organization. This endpoint is optimized for updating custom fields without affecting other organization data.\n\n**Request body:**\n- `customFields`: Object with custom field IDs as keys and their values\n  - Keys should match custom field IDs configured in workspace settings\n  - Values must match the field type (string, number, boolean, date, etc.)\n  - Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields\n\n**Example:**\n```json\n{\n  \"customFields\": {\n    \"field-id-1\": \"Text value\",\n    \"field-id-2\": 123,\n    \"field-id-3\": true,\n    \"field-id-4\": \"2024-01-01\"\n  }\n}\n```\n\n**Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.","operationId":"OrganizationsController_updateOrganizationCustomFields","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customFields":{"type":"object","additionalProperties":true,"description":"Custom field values as key-value pairs"}},"required":["customFields"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization custom fields","tags":["organizations"],"x-required-scopes":["api:write"]}},"/organizations/{id}/invoice-settings":{"get":{"description":"Retrieves invoice and billing settings for a specific organization. These settings control payment terms, reminder rules, and custom text templates for invoices and reminders.\n\n**What are Invoice Settings?**\nInvoice settings allow you to define organization-specific billing parameters that override workspace defaults. This enables customer-specific payment terms, reminder rules, and invoice texts.\n\n**What is returned:**\n- **Payment terms**:\n  - hourRate: Customer-specific hourly rate (used for all billable times)\n  - invoiceDueDays: Number of days until payment is due (1-120)\n  - invoiceLanguage: Language code for invoice generation (e.g., \"en\", \"de\")\n- **Reminder fees**:\n  - enableInvoiceReminderFee: Whether reminder fees are charged\n  - invoiceReminderFee: Amount charged for overdue invoices (0-1000)\n- **Interest on arrears**:\n  - enableInvoiceInterest: Whether interest is charged on overdue invoices\n  - baseInvoiceInterest: Base interest rate percentage (0-1000)\n  - invoiceInterest: Interest rate percentage added to base rate (0-1000)\n- **Custom text templates**: All custom invoice and reminder texts, returned as HTML:\n  - withBestRegards: Closing text for invoices\n  - invoiceGreeting: Greeting text on invoices\n  - invoiceFooter: Footer text on invoices\n  - invoiceReminderFeeWarning: Warning about reminder fees\n  - firstReminderGreeting: Greeting for first reminder\n  - firstReminderFooter: Footer for first reminder\n  - secondReminderGreeting: Greeting for second reminder\n  - secondReminderFooter: Footer for second reminder\n  - cancelationBody: Text for invoice cancellations\n\n**Use cases:**\n- Individual agreements with key clients (longer payment terms, no reminder fees)\n- Customer-specific hourly rates (different rates per customer type)\n- Goodwill rules for important customers (gentler reminders)\n- Stricter rules for risky customers (shorter terms, higher fees)\n- International customers (different interest rates, languages, legal requirements)\n\n**Note:** This endpoint requires the Organizations.view permission. The organization must exist in your workspace and not be deleted. Custom texts are stored internally in IDoc format but returned as HTML for easier consumption.","operationId":"OrganizationsController_getOrganizationInvoiceSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationInvoiceSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get organization invoice settings","tags":["organizations"],"x-required-scopes":["api:read"]},"patch":{"description":"Partially updates invoice and billing settings for an organization. Only the fields you provide will be updated; all other fields remain unchanged.\n\n**What are Invoice Settings?**\nInvoice settings define organization-specific billing parameters that override workspace defaults, enabling customer-specific payment terms, reminder rules, and invoice texts.\n\n**Available settings:**\n- **Payment terms**:\n  - hourRate: Customer-specific hourly rate (number or null)\n  - invoiceDueDays: Days until payment is due, 1-120 (number or null)\n  - invoiceLanguage: Language code for invoices (string or null)\n- **Reminder fees**:\n  - enableInvoiceReminderFee: Enable reminder fees (boolean or null)\n  - invoiceReminderFee: Reminder fee amount, 0-1000 (number or null)\n- **Interest on arrears**:\n  - enableInvoiceInterest: Enable interest on overdue invoices (boolean or null)\n  - baseInvoiceInterest: Base interest rate percentage, 0-1000 (number or null)\n  - invoiceInterest: Interest rate percentage, 0-1000 (number or null)\n- **Custom text templates**: Object with HTML or Markdown content (converted to IDoc internally):\n  - withBestRegards, invoiceGreeting, invoiceFooter\n  - invoiceReminderFeeWarning\n  - firstReminderGreeting, firstReminderFooter\n  - secondReminderGreeting, secondReminderFooter\n  - cancelationBody\n\n**Custom text templates:**\n- Can be provided as HTML or Markdown\n- Will be automatically converted to internal IDoc format\n- Support macros/variables (e.g., #dear, #clientCompanyName, #invoiceNumber)\n- Set to null or empty string to clear a template\n- Omit fields you do not want to change\n\n**Behavior:**\n- Omitted fields remain unchanged\n- Provided fields replace existing values\n- Set fields to null to reset to workspace defaults (where applicable)\n- Custom texts are converted from HTML/Markdown to IDoc format for storage\n\n**What is returned:**\nThe complete updated invoice settings object, with custom texts returned as HTML.\n\n**Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted. Custom texts are stored internally in IDoc format but can be provided as HTML or Markdown.","operationId":"OrganizationsController_patchOrganizationInvoiceSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOrganizationInvoiceSettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationInvoiceSettingsResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update organization invoice settings","tags":["organizations"],"x-required-scopes":["api:write"]}},"/teams":{"get":{"description":"Returns a list of all teams in the workspace with their members.\n\n**What are Teams?**\nTeams group employees together to simplify project access management and form the basis for structured evaluations. Instead of assigning permissions to each employee individually, you can grant entire teams access to specific projects or areas. This makes it easier to manage rights, responsibilities, and resources in one place.\n\nTeams are also used for:\n- Filtering charts and dashboards by team\n- Organizing employees into logical groups\n- Project access management\n- Team-based reporting and insights\n\n**What is returned:**\n- Team identification (ID, name, icon)\n- Number of users in the team\n- Complete list of team members with their details (ID, name, avatar)\n\n**Note:** This endpoint requires the Teams.manage permission.","operationId":"TeamsController_listTeams","parameters":[],"responses":{"200":{"description":"List of teams","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeamResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List all teams","tags":["teams"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new team with the specified name, icon, and members.\n\n**How to create a team:**\n1. Provide a team name (required) - choose any descriptive name\n2. Select an icon emoji (required) - used for visual identification\n3. Assign at least one user to the team (required) - provide an array of user IDs\n\n**Validation rules:**\n- Team name must be a non-empty string\n- Icon must be a non-empty string (typically an emoji)\n- Users array must contain at least one user ID\n- All user IDs must be valid UUIDs of existing employees in the workspace\n\n**What happens after creation:**\n- The team is immediately available for project assignments\n- Team members can be granted access to projects as a group\n- The team appears in team lists and can be used for filtering\n- The team can be edited or deleted later\n\n**What is returned:**\n- Complete team details including the generated team ID\n- All assigned members with their information\n- Team metadata (name, icon, user count)\n\n**Note:** This endpoint requires the Teams.create permission.","operationId":"TeamsController_createTeam","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTeamDto"}}}},"responses":{"200":{"description":"Created team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"name":["Name is required"],"icon":["Icon is required"],"users":["At least one user is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create a new team","tags":["teams"],"x-required-scopes":["api:write"]}},"/teams/{id}":{"get":{"description":"Returns full details of a specific team including all members.\n\n**What is returned:**\n- Team identification (ID, name, icon emoji)\n- Number of users in the team\n- Complete list of all team members with:\n  - User ID\n  - First and last name\n  - Avatar file ID (if available)\n\n**Use cases:**\n- Displaying team information in a detail view\n- Verifying team membership before making changes\n- Getting complete team data for editing\n\n**Note:** Returns 404 if the team does not exist or has been deleted. This endpoint requires the Teams.manage permission.","operationId":"TeamsController_getTeamDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Team details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get team details","tags":["teams"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates only the provided fields of a team. All fields are optional.\n\n**How partial updates work:**\n- Only include the fields you want to change\n- Omitted fields remain unchanged\n- You can update the name, icon, members, or any combination\n\n**Update scenarios:**\n- Change team name: Provide only the \"name\" field\n- Change icon: Provide only the \"icon\" field\n- Update members: Provide only the \"users\" array with new user IDs\n- Update multiple fields: Provide any combination of fields\n\n**Important notes:**\n- When updating members, provide the complete list of user IDs you want in the team\n- The users array replaces the entire team membership (not merged)\n- At least one user must remain in the team after update\n- All user IDs must be valid UUIDs of existing employees\n\n**What is returned:**\n- Complete updated team details\n- All current team members\n- Updated metadata (name, icon, user count)\n\n**Note:** Returns 404 if the team does not exist or has been deleted. This endpoint requires the Teams.edit permission.","operationId":"TeamsController_patchTeam","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTeamDto"}}}},"responses":{"200":{"description":"Updated team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update a team","tags":["teams"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes a team. The team will be marked as deleted but not permanently removed from the database.\n\n**What happens when a team is deleted:**\n- The team is marked as deleted (soft delete)\n- The team no longer appears in team lists\n- Project assignments to this team are removed\n- Historical data and associations are preserved\n- The team cannot be restored through the API\n\n**Important considerations:**\n- Verify the team exists before deletion (endpoint returns 404 if not found)\n- Consider removing team members or project assignments first if needed\n- Deleted teams do not appear in GET requests\n- This operation cannot be undone through the API\n\n**What is returned:**\n- Success confirmation\n\n**Note:** Returns 404 if the team does not exist or has already been deleted. This endpoint requires the Teams.delete permission.","operationId":"TeamsController_deleteTeam","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Team successfully deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete a team","tags":["teams"],"x-required-scopes":["api:write"]}},"/vacations/pending":{"get":{"description":"Retrieves all pending vacation requests that are awaiting manager approval.\n\n**What is Vacation Management?**\nVacation management digitizes the entire vacation process and ensures transparent, traceable, and efficient vacation planning. Employees can submit vacation requests, and managers can review and approve them while considering business workload and capacity.\n\n**What is returned:**\nEach vacation request includes:\n- `id`: Unique vacation request identifier\n- `dateFrom`: Start date of the vacation period (ISO 8601 date string)\n- `dateTo`: End date of the vacation period (ISO 8601 date string)\n- `status`: Current status of the request (always \"Pending\" for this endpoint)\n- `userId`: ID of the employee who requested the vacation\n- `capacity`: Optional capacity utilization percentage for the vacation period\n\n**How it works:**\n- Only vacation requests with status \"Pending\" are returned\n- Managers with \"manageAll\" permission see all pending requests across the workspace\n- Managers with \"manageTeams\" permission only see requests from employees in their teams\n- Use the optional `teamId` query parameter to filter requests for a specific team\n- Results are sorted by start date (earliest first)\n\n**Use cases:**\n- Displaying a list of vacation requests that need approval\n- Building a vacation approval dashboard\n- Filtering requests by team for team-specific managers","operationId":"VacationsController_getPendingVacations","parameters":[{"name":"teamId","required":false,"in":"query","description":"Optional team ID (UUID) to filter vacation requests for a specific team. If not provided, returns requests based on the manager permissions (all teams for manageAll, or manager teams for manageTeams). This parameter is useful for team-specific managers who want to focus on a particular team.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved pending vacations","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get pending vacation requests","tags":["vacations","time-management"],"x-required-scopes":["api:read"]}},"/vacations/upcoming":{"get":{"description":"Retrieves all approved vacations that have not yet ended (end date is today or in the future).\n\n**What is returned:**\nEach vacation includes:\n- `id`: Unique vacation identifier\n- `dateFrom`: Start date of the vacation period (ISO 8601 date string)\n- `dateTo`: End date of the vacation period (ISO 8601 date string)\n- `status`: Current status (always \"Approved\" for this endpoint)\n- `userId`: ID of the employee taking the vacation\n- `capacity`: Optional capacity utilization percentage for the vacation period\n\n**How it works:**\n- Only vacations with status \"Approved\" and end date >= today are returned\n- This includes both current vacations (in progress) and future approved vacations\n- Managers with \"manageAll\" permission see all upcoming vacations across the workspace\n- Managers with \"manageTeams\" permission only see vacations from employees in their teams\n- Use the optional `teamId` query parameter to filter vacations for a specific team\n- Results are sorted by start date (earliest first)\n\n**Use cases:**\n- Displaying an overview of upcoming time off for capacity planning\n- Building a team calendar showing approved absences\n- Identifying potential scheduling conflicts\n- Showing managers which employees will be absent in the near future","operationId":"VacationsController_getUpcomingVacations","parameters":[{"name":"teamId","required":false,"in":"query","description":"Optional team ID (UUID) to filter upcoming vacations for a specific team. If not provided, returns vacations based on the manager permissions (all teams for manageAll, or manager teams for manageTeams). This parameter is useful for team-specific managers who want to focus on a particular team.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved upcoming approved vacations","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get upcoming approved vacations","tags":["vacations","time-management"],"x-required-scopes":["api:read"]}},"/vacations/{id}/approve":{"post":{"description":"Approves a pending vacation request, changing its status from \"Pending\" to \"Approved\".\n\n**What this does:**\n- Updates the vacation request status to \"Approved\"\n- Records who approved the request and when (statusChangedBy, statusChangedAt)\n- Sends a notification to the employee who requested the vacation\n- The approved vacation will now appear in upcoming vacations and team calendars\n\n**How it works:**\n- The vacation must be in \"Pending\" status to be approved\n- Only managers with \"manageAll\" or \"manageTeams\" permission can approve requests\n- Managers with \"manageTeams\" can only approve requests from employees in their teams\n- The vacation ID is provided as a URL parameter\n- Returns 404 if the vacation request is not found\n\n**Use cases:**\n- Approving vacation requests from a management dashboard\n- Bulk approval workflows\n- Automated approval based on business rules\n\n**Note:** After approval, the vacation will be visible in the upcoming vacations endpoint and will affect capacity calculations.","operationId":"VacationsController_approveVacation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Vacation request approved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Vacation request not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Approve a vacation request","tags":["vacations","time-management"],"x-required-scopes":["api:write"]}},"/vacations/{id}/decline":{"post":{"description":"Declines a pending vacation request or cancels an approved vacation.\n\n**What this does:**\n- For pending requests: Changes status from \"Pending\" to \"Rejected\"\n- For approved vacations: Changes status from \"Approved\" to \"Cancelled\"\n- Records who declined/cancelled the request and when (statusChangedBy, statusChangedAt)\n- Optionally records a reason for the decline/cancellation\n- Sends a notification to the employee who requested the vacation\n\n**How it works:**\n- The vacation ID is provided as a URL parameter\n- The optional reason can be provided in the request body\n- Only managers with \"manageAll\" or \"manageTeams\" permission can decline/cancel requests\n- Managers with \"manageTeams\" can only decline/cancel requests from employees in their teams\n- Returns 404 if the vacation request is not found\n- After decline/cancellation, the vacation will appear in the vacation history\n\n**Request body:**\n- `reason` (optional): Text explanation for why the vacation was declined or cancelled\n  - Example: \"Insufficient coverage during this period\"\n  - This reason will be visible in the vacation history\n\n**Use cases:**\n- Rejecting vacation requests that conflict with business needs\n- Cancelling previously approved vacations due to changed circumstances\n- Providing feedback to employees about why their request was not approved","operationId":"VacationsController_declineVacation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclineVacationDto"}}}},"responses":{"200":{"description":"Vacation request declined successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"},"404":{"description":"Vacation request not found"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Decline or cancel a vacation request","tags":["vacations","time-management"],"x-required-scopes":["api:write"]}},"/vacations/history":{"get":{"description":"Retrieves vacation history with support for filtering, sorting, and pagination.\n\n**What is returned:**\nA paginated grid response containing vacation history items. Each item includes:\n- `id`: Unique vacation identifier\n- `dateFrom`: Start date of the vacation period\n- `dateTo`: End date of the vacation period\n- `status`: Vacation status (\"Rejected\", \"Cancelled\", or past \"Approved\" vacations)\n- `employeeId`: ID of the employee who requested the vacation\n- `statusChangedAt`: Timestamp when the status was last changed\n- `statusChangedBy`: ID of the user who changed the status (null if not changed by a user)\n- `reason`: Optional reason provided when the vacation was declined or cancelled\n\n**What is included in history:**\n- All rejected vacation requests\n- All cancelled vacations (previously approved but then cancelled)\n- All approved vacations that have ended (dateTo < today)\n- Does not include pending or upcoming approved vacations (use the pending/upcoming endpoints for those)\n\n**Grid features:**\n- **Filtering**: Filter by date range, employee, status, or any other field\n- **Sorting**: Sort by any column (default sorting can be specified)\n- **Pagination**: Standard grid pagination with page size control\n- Grid parameters are automatically parsed from query string using standard grid format\n\n**How it works:**\n- Managers with \"manageAll\" permission see all vacation history across the workspace\n- Managers with \"manageTeams\" permission only see history from employees in their teams\n- Use the optional `teamId` query parameter to filter history for a specific team\n- Supports standard grid server-side parameters (filters, sort, pagination)\n\n**Use cases:**\n- Building a vacation history table with search and filter capabilities\n- Analyzing vacation patterns and trends\n- Reviewing past vacation decisions and reasons\n- Generating reports on vacation usage","operationId":"VacationsController_getVacationHistory","parameters":[{"name":"teamId","required":false,"in":"query","description":"Optional team ID (UUID) to filter vacation history for a specific team. If not provided, returns history based on the manager permissions (all teams for manageAll, or manager teams for manageTeams). This parameter is useful for team-specific managers who want to focus on a particular team.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved vacation history grid","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object"}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get vacation history grid","tags":["vacations","time-management"],"x-required-scopes":["api:read"]}},"/employees/journal":{"get":{"description":"Retrieves a paginated grid of employee journal entries with filtering and sorting capabilities. Filterable fields: id, employeeId, createdAt, createdBy, mood, reminder. Sortable fields: id, employeeId, createdAt, createdBy, mood, reminder.\n\n**What are Employee Journals?**\nEmployee journals are used to document notes, observations, conversations, and feedback about employees. They help track interactions, performance notes, and important information about team members.\n\n**What is returned:**\n- Journal entry ID and associated employee ID\n- Rich text content (body) converted to HTML format\n- Mood indicator (Happy, Neutral, or Sad)\n- Optional reminder date for follow-ups\n- Creation and update timestamps\n- Creator user ID\n\n**Permission-based filtering:**\n- Users with `Employees.manage` permission: See all journal entries for all employees\n- Users without `Employees.manage` permission: Only see entries for their own employee record where `visibleToSubject = true`\n- Employees can create their own entries, which are automatically marked as visible to themselves\n\n**Filtering options:**\n- Filter by `employeeId` to view entries for a specific employee\n- Filter by `mood` to find entries with specific emotional indicators\n- Filter by `createdAt` or `lastUpdated` to find entries within date ranges\n- Filter by `reminder` to find entries with upcoming or past reminders\n\n**Use cases:**\n- Track conversations and agreements with employees\n- Document feedback and observations about team members\n- Set reminders for follow-up actions\n- Maintain a chronological record of interactions with employees","operationId":"EmployeesJournalController_listEmployeeJournals","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Journal entry ID\n- **employeeId** (string): Employee ID\n- **createdAt** (date): Journal entry creation timestamp\n- **createdBy** (string): Creator user ID\n- **mood** (set): Journal entry mood\n- **reminder** (date): Reminder date\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Journal entry ID\n- **employeeId**: Employee ID\n- **createdAt**: Journal entry creation timestamp\n- **createdBy**: Creator user ID\n- **mood**: Journal entry mood\n- **reminder**: Reminder date\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** createdAt (desc)","schema":{"example":"[{\"field\":\"createdAt\",\"direction\":\"desc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, employeeId, createdAt, createdBy, mood, reminder, body","schema":{"example":"id,employeeId","type":"array","items":{}}}],"responses":{"200":{"description":"Paginated list of employee journal entries"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List employee journal entries","tags":["employees","journal","employees"],"x-required-scopes":["api:read"]},"post":{"description":"Creates a new journal entry for an employee.\n\n**What you need to provide:**\n- `employeeId` (required): UUID of the employee this entry is about\n- `body` (required): Rich text content in HTML or Markdown format. Supports:\n  - Headings, paragraphs, lists, and formatting\n  - Links and embedded content\n  - The content is automatically converted to the internal document format\n- `mood` (optional): Emotional indicator - Happy, Neutral, or Sad (defaults to Neutral)\n- `reminder` (optional): ISO 8601 date string for follow-up reminders (e.g., \"2024-12-31\")\n\n**Access control:**\n- Users with `Employees.edit` permission can create entries for any employee\n- Employees can create entries for their own employee record\n- The `employeeId` must exist and be accessible to the current user\n- Returns 400 if the employee does not exist or is not accessible\n\n**Visibility behavior:**\n- If the user is creating an entry for their own employee record, `visibleToSubject` is automatically set to `true`\n- If the user has `Employees.edit` permission and creates an entry for another employee, `visibleToSubject` is set to `false` (internal only)\n\n**What is returned:**\nThe complete journal entry with all fields, including the body converted to HTML format and timestamps.","operationId":"EmployeesJournalController_createEmployeeJournal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmployeeJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeJournalResponseDto"}}}},"400":{"description":"Validation errors or employee not accessible","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"employeeId":["Employee ID is required","Invalid employeeId or employee not accessible"],"body":["Body content is required"],"mood":["Mood must be one of: Happy, Neutral, Sad"],"reminder":["Reminder must be a valid ISO 8601 date string"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create employee journal entry","tags":["employees","journal","employees"],"x-required-scopes":["api:write"]}},"/employees/journal/{id}":{"get":{"description":"Returns complete details for a specific employee journal entry.\n\n**What is returned:**\n- Journal entry ID and associated employee ID\n- Rich text content (body) converted to HTML format from the internal document format\n- Mood indicator (Happy, Neutral, or Sad)\n- Optional reminder date in ISO 8601 format (null if not set)\n- Creation timestamp (when the entry was created)\n- Last update timestamp (when the entry was last modified)\n- Creator user ID (who created the entry)\n\n**Access control:**\n- Creator of the entry can access\n- Users with `Employees.edit` permission can access any journal entry\n- Returns 404 if the entry does not exist, has been deleted, or the user does not have access\n\n**Note:** The body content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications.","operationId":"EmployeesJournalController_getEmployeeJournalDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeJournalResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get employee journal entry details","tags":["employees","journal","employees"],"x-required-scopes":["api:read"]},"patch":{"description":"Updates specific fields of an existing employee journal entry. Only provided fields are updated; all fields are optional.\n\n**What you can update:**\n- `employeeId` (optional): Change which employee this entry is associated with\n- `body` (optional): Update the rich text content (HTML or Markdown format)\n- `mood` (optional): Change the emotional indicator (Happy, Neutral, or Sad)\n- `reminder` (optional): Update or clear the reminder date\n  - Provide an ISO 8601 date string (e.g., \"2024-12-31\") to set/update\n  - Provide `null` to clear an existing reminder\n  - Omit the field to keep the existing reminder unchanged\n\n**Access control:**\n- Users with `Employees.edit` permission can update any journal entry\n- Employees can update their own entries (creator or subject)\n- If updating `employeeId`, the new employee must exist and be accessible\n- Returns 404 if the entry does not exist, has been deleted, or the user does not have access\n- Returns 400 if the new `employeeId` is invalid or not accessible\n\n**Visibility behavior:**\n- If changing `employeeId` to the user's own employee record, `visibleToSubject` is automatically set to `true`\n- Otherwise, the existing `visibleToSubject` value is preserved\n\n**What is returned:**\nThe complete updated journal entry with all fields, including the body converted to HTML format and updated timestamps.","operationId":"EmployeesJournalController_patchEmployeeJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchEmployeeJournalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeJournalResponseDto"}}}},"400":{"description":"Validation errors or employee not accessible"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Partially update employee journal entry","tags":["employees","journal","employees"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an employee journal entry. The entry is marked as deleted but not permanently removed from the database.\n\n**What happens:**\n- The journal entry is marked with a `deletedAt` timestamp\n- The entry will no longer appear in list queries or be accessible via the API\n- The data is preserved in the database for audit purposes\n- The entry cannot be restored through the API (restoration would require database access)\n\n**Access control:**\n- Users with `Employees.edit` permission can delete any journal entry\n- Employees can delete their own entries (creator or subject)\n- Returns 404 if the entry does not exist, has already been deleted, or the user does not have access\n\n**Note:** This is a soft delete operation. The entry is not permanently removed and can potentially be recovered by database administrators if needed.","operationId":"EmployeesJournalController_deleteEmployeeJournal","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete employee journal entry","tags":["employees","journal","employees"],"x-required-scopes":["api:write"]}},"/employees/grid":{"get":{"description":"Returns a paginated, filterable, and sortable grid of all employees in the workspace.\n\n**What are Employees?**\nEmployees are internal team members from your own company. Each employee can have:\n- A user account with login access (if `canLogin` is true)\n- Team assignments for organizational structure\n- Employment details (entry/exit dates, employment mode, tax class)\n- Personal information (name, email, phone, address, birth date)\n- Professional details (position, title, degree)\n\n**What is returned:**\n- Employee identification (ID, name)\n- Contact information (email, phone, position, title, degree)\n- Complete address details (street, city, ZIP, country, house number)\n- Employment information (employment mode, entry date, exit date, income tax class)\n- Status information (isActive, canLogin, roleId)\n- Team assignments (teamIds array)\n- Personal details (birth date, avatar)\n- Timestamps (createdAt)\n\n**Filtering and search:**\n- Quick search across name, email, phone, position, and address fields\n- Advanced filtering by active status, employment mode, position, role, teams, and many other fields\n- Sorting by any sortable field (default: firstName ascending)\n- Server-side pagination for large employee lists\n\n**Note:** This endpoint requires the `Employees.manage` permission. The employees grid is separate from the contacts grid, which combines employees and organization members.","operationId":"EmployeesController_getEmployeesGrid","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page","schema":{"example":100,"type":"number"}},{"name":"viewId","required":false,"in":"query","description":"View identifier for saved grid configurations","schema":{"example":"all","type":"string"}},{"name":"quickSearch","required":false,"in":"query","description":"Quick search text. Searches across: firstName, lastName, email, phone, position, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber","schema":{"example":"contacts","type":"string"}},{"name":"filters","required":false,"in":"query","description":"JSON string containing filter configuration.\n\n**Available Fields:**\n- **id** (string): Employee ID\n- **firstName** (string): First name\n- **lastName** (string): Last name\n- **email** (string): Email address\n- **phone** (string): Phone number\n- **position** (string): Job position\n- **title** (string): Title (e.g., Dr., Prof.)\n- **degree** (string): Academic degree\n- **addressStreet** (string): Street address\n- **addressZip** (string): ZIP/Postal code\n- **addressCity** (string): City\n- **addressCountry** (string): Country\n- **addressHouseNumber** (string): House number\n- **employmentMode** (string): Employment mode (FullTime, Contract, Temporary, Freelance)\n- **entryDate** (date): Entry date\n- **exitDate** (date): Exit date\n- **incomeTaxClass** (string): Income tax class\n- **birthDate** (date): Birth date\n- **avatarId** (string): Avatar file ID\n- **isActive** (boolean): Whether employee is active (no exitDate or exitDate > today)\n- **canLogin** (boolean): Whether employee can login to the system\n- **roleId** (string): User role ID\n- **teamIds** (array): Team IDs (array)\n- **createdAt** (date): Creation timestamp\n\n**Filter Structure:**\n```json\n[\n  {\n    \"type\": \"string|number|date|set|boolean|array|task_status|task_type\",\n    \"fieldName\": \"field_name\",\n    \"value\": {\n      \"comparison\": \"comparison_type\",\n      \"value\": \"filter_value\"\n    }\n  }\n]\n```\n\n**Group Filters (AND/OR Combinations):**\n```json\n[\n  {\n    \"type\": \"group\",\n    \"fieldName\": \"group0.18807479823070028\",\n    \"value\": {\n      \"type\": \"or\",\n      \"filters\": [\n        {\n          \"type\": \"string\",\n          \"fieldName\": \"fileName\",\n          \"value\": {\n            \"comparison\": \"contain\",\n            \"value\": \"aa\"\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"fieldName\": \"rowCount\",\n          \"value\": {\n            \"comparison\": \">=\",\n            \"value\": 33\n          }\n        }\n      ]\n    }\n  }\n]\n```\n\n**Available Comparison Operators:**\n- **id** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **firstName** (string): First name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **lastName** (string): Last name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **phone** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **position** (string): Job position (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **title** (string): Title (e.g., Dr., Prof.) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **degree** (string): Academic degree (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressStreet** (string): Street address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressZip** (string): ZIP/Postal code (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **addressHouseNumber** (string): House number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **employmentMode** (string): Employment mode (FullTime, Contract, Temporary, Freelance) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **entryDate** (date): Entry date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **exitDate** (date): Exit date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **incomeTaxClass** (string): Income tax class (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **birthDate** (date): Birth date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n- **avatarId** (string): Avatar file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **isActive** (boolean): Whether employee is active (no exitDate or exitDate > today) (boolean filter). Available comparisons: equal, not_equal\n- **canLogin** (boolean): Whether employee can login to the system (boolean filter). Available comparisons: equal, not_equal\n- **roleId** (string): User role ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty\n- **teamIds** (array): Team IDs (array) (array filter). Available comparisons: in, not_in, is_empty, is_not_empty\n- **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty\n\n**Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.","schema":{"example":"[]","type":"string"}},{"name":"sort","required":false,"in":"query","description":"JSON array of sort objects.\n\n**Sortable Fields:**\n- **id**: Employee ID\n- **firstName**: First name\n- **lastName**: Last name\n- **name**: Full name (firstName + lastName, virtual field)\n- **position**: Job position\n- **addressCity**: City\n- **addressCountry**: Country\n- **employmentMode**: Employment mode (FullTime, Contract, Temporary, Freelance)\n- **entryDate**: Entry date\n- **exitDate**: Exit date\n- **birthDate**: Birth date\n- **isActive**: Whether employee is active (no exitDate or exitDate > today)\n- **canLogin**: Whether employee can login to the system\n- **createdAt**: Creation timestamp\n\n**Sort Structure:**\n```json\n[\n  {\n    \"field\": \"field_name\",\n    \"direction\": \"asc|desc\"\n  }\n]\n```\n\n**Default Sort:** firstName (asc)","schema":{"example":"[{\"field\":\"firstName\",\"direction\":\"asc\"}]","type":"string"}},{"name":"fieldsToReturn","required":false,"in":"query","description":"Comma-separated list of field names to include in response items. If not provided, all fields are returned.\n\n**Available Fields:** id, firstName, lastName, name, email, phone, position, title, degree, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber, employmentMode, entryDate, exitDate, incomeTaxClass, birthDate, avatarId, isActive, canLogin, roleId, teamIds, createdAt","schema":{"example":"id,firstName","type":"array","items":{}}}],"responses":{"200":{"description":"Successfully retrieved employees grid"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get employees grid","tags":["employees"],"x-required-scopes":["api:read"]}},"/employees/{id}/calendar-feed":{"get":{"description":"Returns the HTTPS URL for the employee’s Leadtime calendar feed (.ics). Requires a linked user account on the employee.","operationId":"EmployeesController_getEmployeeCalendarFeed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarFeedConfigResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get employee calendar export (ICS subscription URL)","tags":["employees"],"x-required-scopes":["api:read"]}},"/employees/{id}/calendar-feed/regenerate":{"post":{"description":"Issues a new ICS subscription token; previous URLs stop working.","operationId":"EmployeesController_regenerateEmployeeCalendarFeed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarFeedConfigResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Regenerate employee calendar export URL","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/external-calendar-feeds":{"get":{"description":"Returns configured HTTPS ICS feeds that sync busy blocks into Leadtime for this employee.","operationId":"EmployeesController_listEmployeeExternalCalendarFeeds","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalCalendarFeedResponseDto"}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"List employee external calendar imports","tags":["employees"],"x-required-scopes":["api:read"]},"post":{"operationId":"EmployeesController_createEmployeeExternalCalendarFeed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmployeeExternalCalendarFeedDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCalendarFeedResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Add external calendar feed for employee","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/external-calendar-feeds/{feedId}":{"patch":{"operationId":"EmployeesController_patchEmployeeExternalCalendarFeed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"feedId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchEmployeeExternalCalendarFeedDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCalendarFeedResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update employee external calendar feed","tags":["employees"],"x-required-scopes":["api:write"]},"delete":{"operationId":"EmployeesController_deleteEmployeeExternalCalendarFeed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"feedId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Remove employee external calendar feed","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/external-calendar-feeds/{feedId}/sync":{"post":{"operationId":"EmployeesController_syncEmployeeExternalCalendarFeed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"feedId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCalendarFeedResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Sync employee external calendar feed now","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}":{"get":{"description":"Returns complete details for a specific employee, including all personal information, employment data, user account details, team assignments, and working conditions.\n\n**What is returned:**\n- All employee personal information (name, email, phone, address, birth date)\n- Professional details (position, title, degree)\n- Employment information (employment mode, entry date, exit date, income tax class)\n- User account details (userId, roleId, canLogin status)\n- Team assignments (array of team IDs)\n- Avatar URL (if avatar is set)\n- Status flags (isActive calculated from exitDate)\n- Working conditions (chargeableHoursDayGoal)\n- Weekly working time entries (array with dateFrom, workingDays, weeklyWorkingHours)\n- Salary entries (array with dateFrom, salary)\n- Vacation days entries (array with dateFrom, days)\n- Timestamps (createdAt, updatedAt)\n\n**Note:** Returns 404 if the employee does not exist or has been deleted.","operationId":"EmployeesController_getEmployeeDetails","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:read"]},{"bearer":[]}],"summary":"Get employee details","tags":["employees"],"x-required-scopes":["api:read"]},"patch":{"description":"Partially updates an existing employee. Only the fields you provide will be updated; all other fields remain unchanged.\n\n**How updates work:**\n- **Personal information** (firstName, lastName, email, phone, address, birth date, title, degree, position):\n  - Updates the employee record\n  - If email, firstName, or lastName changes, the associated user account is automatically synced\n- **Access settings** (canLogin, roleId):\n  - Updates the user account permissions\n  - If `canLogin` changes from `false` to `true`:\n    - User account is created if it doesn't exist\n    - User status becomes Active\n    - An invitation email is sent automatically\n  - If email is changed and a HelpdeskUser with that email exists, it will be upgraded to Employee type\n- **Team assignments** (teams):\n  - Replaces the entire team assignment list\n  - Provide an empty array to remove all team assignments\n- **Avatar** (avatarId):\n  - Set to a file ID from POST /workspace/upload to update the avatar\n  - Set to `null` to remove the avatar\n- **Working conditions** (chargeableHoursDayGoal):\n  - Updates the chargeable (billable) hours per day goal\n  - Used for time tracking targets and notifications\n  - Set to `null` to remove the goal\n\n**Update process:**\n1. Verify the employee exists (returns 404 if not found)\n2. Update personal information if provided\n3. Update access settings if provided\n4. Update team assignments if provided\n5. Update working conditions if provided\n6. Return the updated employee with all current data\n\n**Note:** All updates are validated and will fail if validation rules are violated (e.g., email must be unique). The response includes the complete updated employee record.","operationId":"EmployeesController_updateEmployee","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchEmployeeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Update employee","tags":["employees"],"x-required-scopes":["api:write"]},"delete":{"description":"Soft deletes an employee and the associated user account. The employee is not permanently removed from the database, but marked as deleted with anonymized data.\n\n**What happens when deleting an employee:**\n1. Employee record is marked as deleted\n2. Associated user account is also marked as deleted\n3. Personal data is anonymized for privacy:\n   - firstName and lastName are set to \"Deleted User\"\n   - Email is anonymized\n   - Other personal information is cleared\n4. Billing is automatically recalculated to reflect the removal\n5. The employee will no longer appear in employee lists or grids\n\n**Note:** This is a soft delete operation. The employee data remains in the database but is anonymized and hidden. This operation cannot be undone. Returns 404 if the employee does not exist.","operationId":"EmployeesController_deleteEmployee","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete employee","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees":{"post":{"description":"Creates a new employee in the workspace with automatic user account creation and team assignments.\n\n**How employee creation works:**\n1. A new employee record is created with the provided information\n2. If `canLogin` is `true`, a user account is automatically created:\n   - User status is set to Active\n   - An invitation email is sent to the employee\n   - The user is assigned the specified role\n3. If an existing HelpdeskUser with the same email exists, it will be upgraded to Employee type\n4. Teams are assigned if provided in the `teams` array\n5. Employee data is automatically synced with the user account\n\n**Required fields:**\n- `firstName`: Employee first name\n- `lastName`: Employee last name\n- `email`: Unique email address (must not exist in the workspace)\n- `roleId`: Role ID to assign to the user account\n\n**Optional fields:**\n- `position`: Job title/position\n- `canLogin`: Whether employee can login (default: false)\n- `teams`: Array of team IDs to assign the employee to\n- `avatarId`: File ID from POST /workspace/upload endpoint\n\n**Avatar upload process:**\n1. First, upload the avatar file using POST /workspace/upload\n2. Get the file ID from the upload response\n3. Use that file ID in the `avatarId` field when creating the employee\n\n**Note:** The email address must be unique within the workspace. If `canLogin` is false, no user account is created initially, but one can be created later by updating the employee.","operationId":"EmployeesController_createEmployee","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmployeeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"400":{"description":"Validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"string","example":"Bad Request"},"errors":{"type":"object","example":{"firstName":["First name is required"],"lastName":["Last name is required"],"email":["Email is required","Email must be valid"],"roleId":["Role ID is required"]}}}}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create employee","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/weekly-working-time":{"post":{"description":"Creates a new weekly working time entry or updates an existing one for an employee.\n\n**How it works:**\n- If `id` is provided in the request body, updates the existing entry with that ID\n- If `id` is omitted, creates a new entry\n\n**What is weekly working time?**\nWeekly working time defines the employee's work schedule:\n- `workingDays`: Array of 7 numbers (Monday=0, Sunday=6), where 1 means the employee works that day, 0 means they don't\n- `weeklyWorkingHours`: Total hours per week (maximum 168)\n- `dateFrom`: Date when this configuration starts applying\n\n**Active entry selection:**\n- The active entry for a given date is the one with the most recent `dateFrom` that is still on or before that date\n- If no entry exists for a date, no entry is considered active\n\n**Returns:** The updated employee object with all weekly working time entries.","operationId":"EmployeesController_createOrUpdateWeeklyWorkingTime","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrUpdateWeeklyWorkingTimeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update weekly working time entry","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/weekly-working-time/{entryId}":{"delete":{"description":"Deletes a weekly working time entry for an employee.\n\n**Returns:** The updated employee object with remaining weekly working time entries.","operationId":"EmployeesController_deleteWeeklyWorkingTime","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete weekly working time entry","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/salaries":{"post":{"description":"Creates a new salary entry or updates an existing one for an employee.\n\n**How it works:**\n- If `id` is provided in the request body, updates the existing entry with that ID\n- If `id` is omitted, creates a new entry\n- Each entry defines a monthly salary amount that applies from a specific date\n\n**Returns:** The updated employee object with all salary entries.","operationId":"EmployeesController_createOrUpdateSalary","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrUpdateSalaryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update salary entry","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/salaries/{entryId}":{"delete":{"description":"Deletes a salary entry for an employee.\n\n**Returns:** The updated employee object with remaining salary entries.","operationId":"EmployeesController_deleteSalary","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete salary entry","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/vacation-days":{"post":{"description":"Creates a new vacation days entry or updates an existing one for an employee.\n\n**How it works:**\n- If `id` is provided in the request body, updates the existing entry with that ID\n- If `id` is omitted, creates a new entry\n- Each entry defines vacation days per year that apply from a specific date\n\n**Returns:** The updated employee object with all vacation days entries.","operationId":"EmployeesController_createOrUpdateVacationDays","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrUpdateVacationDaysDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"400":{"description":"Validation errors"},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Create or update vacation days entry","tags":["employees"],"x-required-scopes":["api:write"]}},"/employees/{id}/vacation-days/{entryId}":{"delete":{"description":"Deletes a vacation days entry for an employee.\n\n**Returns:** The updated employee object with remaining vacation days entries.","operationId":"EmployeesController_deleteVacationDays","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication token"},"403":{"description":"Forbidden - Insufficient API scopes or permissions"}},"security":[{"OAuth2":["api:write"]},{"bearer":[]}],"summary":"Delete vacation days entry","tags":["employees"],"x-required-scopes":["api:write"]}},"/automations/webhooks/{secret}":{"post":{"summary":"Trigger automation via webhook","description":"Unauthenticated inbound webhook for automation triggers. No Bearer token required. The secret is webhookSecret from a webhook trigger (returned in automation triggers after create/update). POST with Content-Type: application/json; request body is passed to the agent as context. Returns 202 with { \"accepted\": true } when the secret matches an enabled webhook trigger and the automation executor is eligible. Returns 202 with { \"accepted\": false } when the secret is valid but the executor is inactive or no longer in the workspace (no run is enqueued).","operationId":"AutomationWebhooks_handleWebhook","tags":["automations"],"parameters":[{"name":"secret","in":"path","required":true,"schema":{"type":"string"},"description":"webhookSecret from the automation trigger"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","description":"Arbitrary JSON; passed to the agent as webhook payload"}}}},"responses":{"202":{"description":"Request processed: accepted true if a run was enqueued; accepted false if the executor cannot run automations","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"boolean","description":"True when the automation run was queued; false when the executor is ineligible"}}}}}},"404":{"description":"Webhook not found (invalid or disabled secret)"}},"servers":[{"url":"https://leadtime.app/api","description":"API base for webhook endpoints"}],"security":[]}}},"info":{"title":"Leadtime Public API","description":"Versioned public API.\n\nAuthentication:\n- Personal Access Tokens (PAT): Create/manage in your Profile → API Tokens. Use the raw token once returned to you as Bearer: `Authorization: Bearer <pat>`.\n- OAuth 2.0 (Authorization Code + PKCE): Workspace admin creates an OAuth client in Workspace Settings → API & Integrations, then obtain tokens via the standard OAuth flow. Use the access token as Bearer. Refresh tokens are supported at `/api/oauth/token`.\n\nDynamic Client Registration (RFC 7591):\nThe API supports RFC 7591 compliant dynamic client registration, allowing applications to register OAuth clients programmatically without requiring workspace admin setup.\n\n**Registration Endpoint**: `POST /api/oauth/register`\n\n**Features**:\n- Automatically creates OAuth clients with generated `client_id` and optional `client_secret`\n- Clients are auto-installed in workspaces when the API feature is enabled\n- Supports multiple authentication methods: `none` (public clients), `client_secret_post`, `client_secret_basic`\n- Idempotent registration: If a client with the same name and redirect URIs already exists, the existing client is updated instead of creating a duplicate\n- Returns RFC 7591 compliant registration response with client credentials and metadata\n\n**Request Body**:\n- `redirect_uris` (required): Array of redirect URIs for the client\n- `client_name` (optional): Display name for the client (defaults to \"Dynamically Registered Client\")\n- `client_uri` (optional): Homepage URL of the client application\n- `logo_uri` (optional): Logo URL for the client\n- `token_endpoint_auth_method` (optional): Authentication method (`none`, `client_secret_post`, or `client_secret_basic`, defaults to `none`)\n\n**Response**:\n- `client_id`: Unique client identifier (format: `dcr_<uuid>`)\n- `client_secret`: Client secret (only returned for confidential clients, not for `none` auth method)\n- `client_name`: Client display name\n- `redirect_uris`: Array of allowed redirect URIs\n- `token_endpoint_auth_method`: Authentication method used\n- `grant_types`: Supported grant types (`authorization_code`, `refresh_token`)\n- `response_types`: Supported response types (`code`)\n- `client_id_issued_at`: Unix timestamp when client was created\n- `client_secret_expires_at`: Expiration timestamp (0 means no expiration)\n- `registration_client_uri`: URI for client registration management\n\n**Discovery**:\nThe registration endpoint URL is available via OAuth 2.0 Authorization Server Metadata at `/api/.well-known/oauth-authorization-server` (see `registration_endpoint` field).\n\nImposter Mode (Testing/Debugging):\n⚠️ **Permission Required**: `PublicApi.allowImposterMode`\n\nOptional headers for testing and debugging (case-insensitive):\n- `LT_IMPOSTER_USER_ID` / `lt_imposter_user_id`: Override authenticated user with specified user ID. User must exist and belong to the same workspace. Original token permissions are preserved.\n- `LT_CUSTOM_NOW` / `lt_custom_now`: Override current time with custom date (ISO 8601 format). Useful for testing time-sensitive operations.\n\nExample:\n```\ncurl -X GET \"https://api.example.com/api/public/account/info\" \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"LT_IMPOSTER_USER_ID: user-123\" \\\n  -H \"LT_CUSTOM_NOW: 2024-01-15T10:00:00Z\"\n```\n\nBase URL: https://leadtime.app/api/public","version":"1.0","contact":{}},"tags":[],"servers":[{"url":"https://leadtime.app/api/public","description":"Leadtime Public API Server"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"},"OAuth2":{"type":"oauth2","flows":{"authorizationCode":{"authorizationUrl":"https://leadtime.app/api/oauth/authorize","tokenUrl":"https://leadtime.app/api/oauth/token","refreshUrl":"https://leadtime.app/api/oauth/token","scopes":{"api:read":"api:read","api:write":"api:write"}}}}},"schemas":{"AccountInfoResponse":{"type":"object","properties":{"ok":{"type":"boolean","example":true,"description":"Operation success flag"},"userId":{"type":"string","example":"user_123","description":"Current user ID"},"workspaceId":{"type":"string","example":"workspace_456","description":"Current workspace ID"},"email":{"type":"string","example":"jane.doe@example.com"},"firstName":{"type":"object","example":"Jane","nullable":true},"lastName":{"type":"object","example":"Doe","nullable":true},"name":{"type":"string","example":"Jane Doe","description":"Convenience full name"},"type":{"type":"string","enum":["Employee","OrganizationMember","Bot","LeadtimeSupport","HelpdeskUser"],"description":"User type"},"roleId":{"type":"string","example":"role_abc"},"roleName":{"type":"string","example":"Admin"},"permissions":{"description":"Granted permission keys","type":"array","items":{"type":"string"}},"avatarUrl":{"type":"object","example":"https://app.example.com/api/files/public/avatar_123","nullable":true,"description":"Public URL for user avatar image"},"employeeId":{"type":"object","example":"emp_789","nullable":true,"description":"Employee ID if user is Employee"},"memberId":{"type":"object","example":null,"nullable":true,"description":"Organization member ID if user is OrganizationMember"},"source":{"type":"string","enum":["webapp","api_pat","api_oauth","mcp","agent"],"description":"Authentication source"},"language":{"type":"string","example":"en","description":"User preferred language"},"emailNotifications":{"type":"boolean","example":true,"description":"Email notifications enabled"},"userWorkspaceColor":{"type":"string","enum":["INHERIT","DEFAULT","BLACK","RED","ROSE","MAGENTA","GREEN","BLUE","YELLOW","VIOLET"],"description":"User workspace color preference"}},"required":["ok","userId","workspaceId","email","firstName","lastName","name","type","roleId","roleName","permissions","avatarUrl","employeeId","memberId","source","language","emailNotifications","userWorkspaceColor"]},"EmployeeDataResponse":{"type":"object","properties":{"id":{"type":"string","example":"emp_123","description":"Employee ID"},"firstName":{"type":"object","example":"John","nullable":true,"description":"First name"},"lastName":{"type":"object","example":"Doe","nullable":true,"description":"Last name"},"email":{"type":"object","example":"john.doe@example.com","nullable":true,"description":"Email address"},"title":{"type":"object","example":"Software Engineer","nullable":true,"description":"Job title"},"degree":{"type":"object","example":"Bachelor","nullable":true,"description":"Degree"},"addressStreet":{"type":"object","example":"Main Street","nullable":true,"description":"Street address"},"addressZip":{"type":"object","example":"12345","nullable":true,"description":"ZIP code"},"addressCity":{"type":"object","example":"Berlin","nullable":true,"description":"City"},"addressCountry":{"type":"object","example":"Germany","nullable":true,"description":"Country"},"addressHouseNumber":{"type":"object","example":"12","nullable":true,"description":"House number"},"phone":{"type":"object","example":"+49 123 456789","nullable":true,"description":"Phone number"},"birthDate":{"type":"object","example":"1990-01-01T00:00:00.000Z","nullable":true,"description":"Birth date"},"signatureUrl":{"type":"object","example":"https://app.example.com/api/files/public/signature_123","nullable":true,"description":"Signature file URL"}},"required":["id","firstName","lastName","email","title","degree","addressStreet","addressZip","addressCity","addressCountry","addressHouseNumber","phone","birthDate","signatureUrl"]},"UpdateWorkspaceColorRequest":{"type":"object","properties":{"color":{"type":"string","enum":["INHERIT","DEFAULT","BLACK","RED","ROSE","MAGENTA","GREEN","BLUE","YELLOW","VIOLET"],"description":"New workspace color"}},"required":["color"]},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Operation success flag"}},"required":["success"]},"UpdateLanguageRequest":{"type":"object","properties":{"language":{"type":"string","example":"en","description":"Preferred language code","enum":["en","de"]}},"required":["language"]},"UpdateEmailNotificationsRequest":{"type":"object","properties":{"enabled":{"type":"boolean","example":true,"description":"Enable email notifications"}},"required":["enabled"]},"UpdateEmployeeDataRequest":{"type":"object","properties":{"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"title":{"type":"string"},"degree":{"type":"string"},"addressStreet":{"type":"string"},"addressZip":{"type":"string"},"addressCity":{"type":"string"},"addressCountry":{"type":"string","example":"US","description":"ISO 3166-1 alpha-2 country code (2 uppercase letters)"},"addressHouseNumber":{"type":"string"},"phone":{"type":"string"},"birthDate":{"type":"string","description":"ISO8601 date string"},"signatureFileId":{"type":"string","description":"File ID for signature (upload via POST /api/public/workspace/upload first)"},"avatarId":{"type":"object","description":"File ID for avatar (upload via POST /api/public/workspace/upload first). Set to null to remove avatar.","nullable":true}}},"BankDataResponse":{"type":"object","properties":{"id":{"type":"string","example":"emp_123","description":"Employee ID"},"bankAccountBIC":{"type":"object","example":"DEUTDEFF","nullable":true,"description":"Bank BIC code"},"bankAccountIBAN":{"type":"object","example":"DE89370400440532013000","nullable":true,"description":"Bank IBAN"},"bankAccountName":{"type":"object","example":"Deutsche Bank","nullable":true,"description":"Bank account name"},"bankAccountOwner":{"type":"object","example":"John Doe","nullable":true,"description":"Account owner name"}},"required":["id","bankAccountBIC","bankAccountIBAN","bankAccountName","bankAccountOwner"]},"UpdateBankDataRequest":{"type":"object","properties":{"bankAccountBIC":{"type":"object","example":"DEUTDEFF","description":"Bank BIC code"},"bankAccountIBAN":{"type":"object","example":"DE89370400440532013000","description":"Bank IBAN"},"bankAccountOwner":{"type":"object","example":"John Doe","description":"Account owner name"},"bankAccountName":{"type":"object","example":"Deutsche Bank","description":"Bank name"}}},"OrgMemberDataResponse":{"type":"object","properties":{"id":{"type":"string","example":"member_123","description":"Organization member ID"},"firstName":{"type":"object","example":"John","nullable":true,"description":"First name"},"lastName":{"type":"object","example":"Doe","nullable":true,"description":"Last name"},"email":{"type":"object","example":"john.doe@example.com","nullable":true,"description":"Email address"},"title":{"type":"object","example":"Software Engineer","nullable":true,"description":"Job title"},"degree":{"type":"object","example":"Bachelor","nullable":true,"description":"Degree"},"position":{"type":"object","example":"Senior Developer","nullable":true,"description":"Position"},"addressStreet":{"type":"object","example":"Main Street","nullable":true,"description":"Street address"},"addressZip":{"type":"object","example":"12345","nullable":true,"description":"ZIP code"},"addressCity":{"type":"object","example":"Berlin","nullable":true,"description":"City"},"addressCountry":{"type":"object","example":"Germany","nullable":true,"description":"Country"},"addressHouseNumber":{"type":"object","example":"12","nullable":true,"description":"House number"},"phone":{"type":"object","example":"+49 123 456789","nullable":true,"description":"Phone number"},"birthDate":{"type":"object","example":"1990-01-01T00:00:00.000Z","nullable":true,"description":"Birth date"}},"required":["id","firstName","lastName","email","title","degree","position","addressStreet","addressZip","addressCity","addressCountry","addressHouseNumber","phone","birthDate"]},"UpdateOrgMemberDataRequest":{"type":"object","properties":{"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"title":{"type":"string"},"degree":{"type":"string"},"position":{"type":"string"},"addressStreet":{"type":"string"},"addressZip":{"type":"string"},"addressCity":{"type":"string"},"addressCountry":{"type":"string","example":"DE","description":"ISO 3166-1 alpha-2 country code (2 uppercase letters)"},"addressHouseNumber":{"type":"string"},"phone":{"type":"string"},"birthDate":{"type":"string","description":"ISO8601 date string"},"avatarId":{"type":"object","description":"File ID for avatar (upload via POST /api/public/workspace/upload first). Set to null to remove avatar.","nullable":true}}},"FutureVacationResponse":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"dateFrom":{"type":"string","example":"2024-06-01"},"dateTo":{"type":"string","example":"2024-06-05"},"status":{"type":"string","example":"Approved","enum":["Pending","Approved","Rejected"]}},"required":["id","dateFrom","dateTo","status"]},"VacationStatsResponse":{"type":"object","properties":{"year":{"type":"number","example":2024},"byContract":{"type":"number","example":25},"daysPeriods":{"example":[{"from":"2024-01-01T00:00:00.000Z","to":"2024-12-31T23:59:59.999Z","days":25}],"type":"array","items":{"type":"string"}},"pendingVacationDays":{"type":"number","example":5},"approvedFutureVacationDays":{"type":"number","example":10},"approvedPastVacationDays":{"type":"number","example":8},"fromOvertime":{"type":"number","example":2},"added":{"type":"number","example":0},"deducted":{"type":"number","example":0},"totalVacationDays":{"type":"number","example":27},"totalUsedVacationDays":{"type":"number","example":8},"totalRemainingVacationDays":{"type":"number","example":19},"potentialVacationDays":{"type":"number","example":3}},"required":["year","byContract","daysPeriods","pendingVacationDays","approvedFutureVacationDays","approvedPastVacationDays","fromOvertime","added","deducted","totalVacationDays","totalUsedVacationDays","totalRemainingVacationDays"]},"VacationHistoryResponse":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"dateFrom":{"type":"string","example":"2024-06-01T00:00:00.000Z"},"dateTo":{"type":"string","example":"2024-06-05T00:00:00.000Z"},"status":{"type":"string","example":"Approved","enum":["Pending","Approved","Rejected"]},"employeeId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174001"},"statusChangedAt":{"type":"string","example":"2024-06-02T10:30:00.000Z","nullable":true},"statusChangedBy":{"type":"object","example":"John Doe","nullable":true},"reason":{"type":"object","example":"Family vacation","nullable":true}},"required":["id","dateFrom","dateTo","status","employeeId","statusChangedAt","statusChangedBy","reason"]},"VacationHistoryGridResponse":{"type":"object","properties":{"total":{"type":"number","example":50},"page":{"type":"number","example":1},"pageSize":{"type":"number","example":10},"items":{"type":"array","items":{"$ref":"#/components/schemas/VacationHistoryResponse"}}},"required":["total","page","pageSize","items"]},"VacationCompensationResponse":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"employeeId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174001"},"type":{"type":"string","example":"Overtime","enum":["Overtime","Unused","Bonus"]},"year":{"type":"number","example":2024},"toYear":{"type":"number","example":2025},"days":{"type":"number","example":5},"createdAt":{"type":"string","example":"2024-06-01T10:30:00.000Z"}},"required":["id","employeeId","type","year","toYear","days","createdAt"]},"VacationCompensationGridResponse":{"type":"object","properties":{"total":{"type":"number","example":25},"page":{"type":"number","example":1},"pageSize":{"type":"number","example":10},"items":{"type":"array","items":{"$ref":"#/components/schemas/VacationCompensationResponse"}}},"required":["total","page","pageSize","items"]},"RequestVacationDto":{"type":"object","properties":{"dateFrom":{"format":"date-time","type":"string","example":"2024-06-01T00:00:00.000Z","description":"Start date of vacation"},"dateTo":{"format":"date-time","type":"string","example":"2024-06-05T00:00:00.000Z","description":"End date of vacation"}},"required":["dateFrom","dateTo"]},"SicknessRowResponse":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"dateFrom":{"type":"string","example":"2024-06-01T00:00:00.000Z"},"dateTo":{"type":"string","example":"2024-06-03T00:00:00.000Z"},"employeeId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174001"},"comment":{"type":"object","example":"Flu symptoms","nullable":true},"attachedFilesIds":{"example":["file1.pdf","file2.jpg"],"type":"array","items":{"type":"string"}}},"required":["id","dateFrom","dateTo","employeeId","comment","attachedFilesIds"]},"SicknessGridResponse":{"type":"object","properties":{"total":{"type":"number","example":15},"page":{"type":"number","example":1},"pageSize":{"type":"number","example":10},"items":{"type":"array","items":{"$ref":"#/components/schemas/SicknessRowResponse"}}},"required":["total","page","pageSize","items"]},"RequestSicknessDto":{"type":"object","properties":{"dateFrom":{"type":"string","example":"2024-06-01T00:00:00.000Z","description":"Start date of sickness"},"dateTo":{"type":"string","example":"2024-06-03T00:00:00.000Z","description":"End date of sickness"},"comment":{"type":"string","example":"Flu symptoms","description":"Optional comment about the sickness"},"attachedFilesIds":{"description":"Array of file IDs for medical certificates (upload via POST /api/public/workspace/upload first)","type":"array","items":{"type":"string"}}},"required":["dateFrom","dateTo"]},"JournalEntryResponse":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"createdAt":{"type":"string","example":"2024-06-01T10:30:00.000Z"},"createdBy":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174001","description":"ID of the user who created the journal entry"},"mood":{"type":"object","example":"Happy","description":"Mood of the journal entry"},"reminder":{"type":"object","example":"2024-06-15T09:00:00.000Z","description":"Reminder date"},"body":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"}},"required":["id","createdAt","createdBy","body"]},"JournalGridResponse":{"type":"object","properties":{"items":{"description":"Array of journal entries","type":"array","items":{"$ref":"#/components/schemas/JournalEntryResponse"}},"total":{"type":"number","example":150,"description":"Total number of journal entries"},"page":{"type":"number","example":1,"description":"Current page number"},"pageSize":{"type":"number","example":20,"description":"Number of items per page"},"viewId":{"type":"string","example":"default","description":"View ID for the grid"}},"required":["items","total","page","pageSize"]},"CreateJournalDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Journal entry ID for updates"},"mood":{"type":"string","example":"Happy","enum":["Sad","Neutral","Happy"],"description":"Mood of the journal entry"},"reminder":{"type":"string","example":"2024-06-15T09:00:00.000Z","description":"Reminder date"},"body":{"type":"string","example":"<p>Today was a great day!</p>","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"}},"required":["body"]},"PatchJournalDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Journal entry ID for updates"},"mood":{"type":"string","example":"Happy","enum":["Sad","Neutral","Happy"],"description":"Mood of the journal entry"},"reminder":{"type":"string","example":"2024-06-15T09:00:00.000Z","description":"Reminder date"},"body":{"type":"string","example":"<p>Today was a great day!</p>","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"}}},"UpdateVacationRequest":{"type":"object","properties":{"dateFrom":{"type":"string","example":"2024-06-01T00:00:00.000Z","description":"Vacation start date"},"dateTo":{"type":"string","example":"2024-06-05T00:00:00.000Z","description":"Vacation end date"}},"required":["dateFrom","dateTo"]},"UpdateSicknessRequest":{"type":"object","properties":{"dateFrom":{"type":"string","example":"2024-06-01T00:00:00.000Z","description":"Sickness start date"},"dateTo":{"type":"string","example":"2024-06-05T00:00:00.000Z","description":"Sickness end date"},"comment":{"type":"string","example":"Flu symptoms","description":"Comment about the sickness"},"attachedFilesIds":{"example":["file-id-1","file-id-2"],"description":"Array of attached file IDs","type":"array","items":{"type":"string"}}},"required":["dateFrom","dateTo","attachedFilesIds"]},"OvertimeStatsResponse":{"type":"object","properties":{"startDate":{"format":"date-time","type":"string","example":"2023-01-01T00:00:00.000Z","description":"Start date of employment"},"workingSince":{"format":"date-time","type":"string","example":"2023-01-15T00:00:00.000Z","description":"Date when employee started working"},"totalHoursLogged":{"type":"number","example":1750.5,"description":"Total hours logged"},"totalCompensatedHours":{"type":"number","example":50,"description":"Total compensated hours"},"workingHours":{"type":"number","example":1600,"description":"Expected working hours"},"workingDays":{"type":"number","example":200,"description":"Total working days"},"vacationThisYearHours":{"type":"number","example":80,"description":"Vacation hours taken this year"},"paidThisYearHours":{"type":"number","example":40,"description":"Paid vacation hours this year"},"unpaidThisYearHours":{"type":"number","example":40,"description":"Unpaid vacation hours this year"},"vacationDaysThisYear":{"type":"number","example":10,"description":"Vacation days taken this year"},"balance":{"type":"number","example":100.5,"description":"Current overtime balance in hours"}},"required":["startDate","workingSince","totalHoursLogged","totalCompensatedHours","workingHours","workingDays","vacationThisYearHours","paidThisYearHours","unpaidThisYearHours","vacationDaysThisYear","balance"]},"OvertimeCompensationResponse":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Compensation record ID"},"employeeId":{"type":"string","example":"emp_789","description":"Employee ID"},"type":{"type":"string","example":"PAID","description":"Compensation type"},"hours":{"type":"number","example":8,"description":"Number of hours compensated"},"vacationYear":{"type":"object","example":2024,"nullable":true,"description":"Vacation year if applicable"},"vacationDays":{"type":"object","example":1,"nullable":true,"description":"Number of vacation days if applicable"},"createdAt":{"format":"date-time","type":"string","example":"2024-03-15T10:30:00.000Z","description":"Compensation creation date"}},"required":["id","employeeId","type","hours","vacationYear","vacationDays","createdAt"]},"OvertimeCompensationGridResponse":{"type":"object","properties":{"total":{"type":"number","example":25},"page":{"type":"number","example":1},"pageSize":{"type":"number","example":10},"items":{"type":"array","items":{"$ref":"#/components/schemas/OvertimeCompensationResponse"}}},"required":["total","page","pageSize","items"]},"ClaimAgentConnectorSetupDto":{"type":"object","properties":{"setupToken":{"type":"string","description":"One-time setup token generated from the bot settings page.","example":"lt_conn_abc123"},"gatewayPublicUrl":{"type":"string","description":"Public OpenClaw/Hermes gateway URL reachable by Leadtime for webhook delivery.","example":"https://agent.example.com"},"agentId":{"type":"string","description":"Runtime-specific agent id to attach this Leadtime bot to.","example":"main"},"runtimeVersion":{"type":"string","description":"Connector/runtime version reported by the installer.","example":"openclaw-leadtime-plugin@0.1.0"}},"required":["setupToken","gatewayPublicUrl"]},"AppendAgentSessionActivityDto":{"type":"object","properties":{"activityType":{"type":"string","enum":["thought","action","elicitation","response","error","prompt","log","modelRequest","modelResponse","toolCall","toolResult"]},"body":{"type":"string"},"action":{"type":"string"},"parameter":{"type":"string"},"result":{"type":"string"},"providerEventId":{"type":"string"},"providerEventType":{"type":"string"},"raw":{"type":"object"},"idempotencyKey":{"type":"string"}},"required":["activityType","body"]},"UpdateAgentSessionStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["queued","running","waitingInput","waitingApproval","done","failed","canceled"]},"message":{"type":"string"},"idempotencyKey":{"type":"string"}},"required":["status"]},"ToggleFavoriteRequestDto":{"type":"object","properties":{"entityId":{"type":"string","description":"The unique identifier (UUID) of the entity to favorite or unfavorite. This is the ID of the task, project, organization, or command you want to toggle.","example":"550e8400-e29b-41d4-a716-446655440000"},"entityType":{"type":"string","description":"The type of entity being favorited. Determines which entity table to check and what metadata to return. Valid values: Project, Task, Organization, Command.","enum":["Project","Task","Organization","Command"],"example":"Task"}},"required":["entityId","entityType"]},"ToggleFavoriteResponseDto":{"type":"object","properties":{"isFavorite":{"type":"boolean","description":"Whether the entity is now favorited after the toggle operation. True means the entity was added to favorites, false means it was removed.","example":true}},"required":["isFavorite"]},"FavoritesListResponseDto":{"type":"object","properties":{"favorites":{"description":"Array of all favorites for the authenticated user, sorted by custom sort order (if set) or creation date. Each favorite includes entity-specific metadata. Deleted entities are automatically excluded from the results.","items":{"type":"array"},"type":"array"}},"required":["favorites"]},"FormCategoryTranslationResponseDto":{"type":"object","properties":{"language":{"type":"string"},"name":{"type":"object","nullable":true},"description":{"type":"object","nullable":true}},"required":["language"]},"FormCategoryResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"icon":{"type":"string"},"description":{"type":"object","nullable":true},"sort":{"type":"number"},"defaultKey":{"type":"object","nullable":true},"translations":{"type":"array","items":{"$ref":"#/components/schemas/FormCategoryTranslationResponseDto"}}},"required":["id","name","icon","sort","translations"]},"FormCategoryTranslationRequestDto":{"type":"object","properties":{"language":{"type":"string","example":"en"},"name":{"type":"object","nullable":true},"description":{"type":"object","nullable":true}},"required":["language"]},"CreateFormCategoryDto":{"type":"object","properties":{"name":{"type":"string"},"icon":{"type":"string","example":":clipboard:"},"description":{"type":"object","nullable":true},"translations":{"type":"array","items":{"$ref":"#/components/schemas/FormCategoryTranslationRequestDto"}}},"required":["name","icon","translations"]},"UpdateFormCategoryDto":{"type":"object","properties":{"name":{"type":"string"},"icon":{"type":"string"},"description":{"type":"object","nullable":true},"translations":{"type":"array","items":{"$ref":"#/components/schemas/FormCategoryTranslationRequestDto"}}}},"SortFormCategoriesDto":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]},"SortFormTemplatesDto":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]},"FormTemplateFieldResponseDto":{"type":"object","properties":{"id":{"type":"string"},"templateId":{"type":"string"},"sort":{"type":"number"},"key":{"type":"object","nullable":true,"description":"When set, this string is the `valueKey` for PATCH `/tasks/{identifier}/form-instances/{formInstanceId}` payloads; when null, use the field `id` instead."},"label":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url","SectionHeading","FileUpload","Signature","Radio"]},"placeholder":{"type":"object","nullable":true},"helpText":{"type":"object","nullable":true},"required":{"type":"boolean"},"settings":{"type":"object","description":"Type-specific settings JSON"},"selectOptions":{"type":"array","items":{"type":"object"},"description":"Options for Select, MultiSelect, Radio"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","templateId","sort","label","description","type","required","settings","selectOptions","createdAt","updatedAt"]},"SortFormTemplateFieldsDto":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]},"CreateFormTemplateFieldDto":{"type":"object","properties":{"sort":{"type":"number"},"key":{"type":"object","nullable":true},"label":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url","SectionHeading","FileUpload","Signature","Radio"]},"placeholder":{"type":"object","nullable":true},"helpText":{"type":"object","nullable":true},"required":{"type":"boolean"},"settings":{"type":"object"},"selectOptions":{"type":"array","items":{"type":"object"}}},"required":["label","type"]},"UpdateFormTemplateFieldDto":{"type":"object","properties":{"sort":{"type":"number"},"key":{"type":"object","nullable":true},"label":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url","SectionHeading","FileUpload","Signature","Radio"]},"placeholder":{"type":"object","nullable":true},"helpText":{"type":"object","nullable":true},"required":{"type":"boolean"},"settings":{"type":"object"},"selectOptions":{"type":"array","items":{"type":"object"}}}},"FormTemplateListItemResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"categoryId":{"type":"string"},"tags":{"description":"Assigned form-template tag IDs (UUIDs).","type":"array","items":{"type":"string"}},"icon":{"type":"object","nullable":true},"sort":{"type":"number"},"fieldsCount":{"type":"number","description":"Number of non-deleted fields on this template."},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"updatedBy":{"type":"object","nullable":true},"category":{"type":"object"}},"required":["id","name","description","categoryId","tags","sort","fieldsCount","createdAt","updatedAt","createdBy","category"]},"FormTemplateDetailResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"categoryId":{"type":"string"},"tags":{"description":"Assigned form-template tag IDs (UUIDs).","type":"array","items":{"type":"string"}},"icon":{"type":"object","nullable":true},"sort":{"type":"number"},"fieldsCount":{"type":"number","description":"Number of non-deleted fields on this template."},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"updatedBy":{"type":"object","nullable":true},"category":{"type":"object"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FormTemplateFieldResponseDto"}}},"required":["id","name","description","categoryId","tags","sort","fieldsCount","createdAt","updatedAt","createdBy","category","fields"]},"CreateFormTemplateDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"categoryId":{"type":"string"},"tags":{"description":"Tag IDs (UUIDs) of type form-template. List or create tags via GET/POST `tags/form-template/list` and `tags/form-template/create`.","type":"array","items":{"type":"string"}},"icon":{"type":"object","nullable":true}},"required":["name","categoryId"]},"UpdateFormTemplateDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"categoryId":{"type":"string"},"tags":{"description":"Tag IDs (UUIDs) of type form-template. List tags via GET `tags/form-template/list`.","type":"array","items":{"type":"string"}},"icon":{"type":"object","nullable":true}}},"AttendanceRequestDto":{"type":"object","properties":{"date":{"type":"string","description":"Date of the workday in ISO 8601 format (YYYY-MM-DD). This is the date for which attendance is being recorded.","example":"2025-01-15"},"timeFrom":{"type":"string","description":"Clock-in time (start of work) in HH:mm format (24-hour format). This is when the employee started working. Example: \"09:00\" for 9 AM, \"13:30\" for 1:30 PM.","example":"09:00","pattern":"^([0-1][0-9]|2[0-3]):[0-5][0-9]$"},"timeTo":{"type":"string","description":"Clock-out time (end of work) in HH:mm format (24-hour format). Optional - if not provided, you can clock out later. Must be after timeFrom if provided. Example: \"17:30\" for 5:30 PM.","example":"17:30","pattern":"^([0-1][0-9]|2[0-3]):[0-5][0-9]$"},"mood":{"type":"string","description":"Employee mood for the workday. Indicates how the employee felt during the day: Good (positive), Neutral (normal), or Bad (challenging).","enum":["Good","Neutral","Bad"],"example":"Good"},"comment":{"type":"string","description":"Optional notes or comments about the workday. Can include information about the work performed, special circumstances, or any relevant details.","example":"Regular workday"}},"required":["date","timeFrom","mood"]},"AttendanceResponseDto":{"type":"object","properties":{"date":{"type":"string","description":"Date of the attendance record","example":"2025-01-15T00:00:00.000Z"},"timeFrom":{"type":"string","description":"Clock-in time (when work started). ISO 8601 datetime format. Null if not yet clocked in.","example":"1970-01-01T09:00:00.000Z","nullable":true},"timeTo":{"type":"string","description":"Clock-out time (when work ended). ISO 8601 datetime format. Null if not yet clocked out.","example":"1970-01-01T17:30:00.000Z","nullable":true},"totalHours":{"type":"number","description":"Total hours worked, automatically calculated from clock-in and clock-out times. Null if end time is not yet recorded.","example":8.5,"nullable":true},"mood":{"type":"string","description":"Employee mood for the workday (Good, Neutral, or Bad). Indicates how the employee felt during the day.","enum":["Good","Neutral","Bad"],"example":"Good","nullable":true},"comment":{"type":"string","description":"Optional notes or comments about the workday. Can include information about work performed or special circumstances.","example":"Regular workday","nullable":true},"type":{"type":"string","description":"Type of attendance record. Indicates the nature of the workday (e.g., Workday, Holiday, etc.).","enum":["Workday","Vacation","VacationPending","Sick","Away"],"example":"Workday"},"deletedAt":{"type":"string","description":"Deletion timestamp (null if not deleted)","example":null,"nullable":true}},"required":["date","timeFrom","timeTo","totalHours","mood","comment","type","deletedAt"]},"DeleteAttendanceRequestDto":{"type":"object","properties":{"date":{"type":"string","description":"Date of the attendance record to delete in ISO 8601 format (YYYY-MM-DD). The attendance record for this date will be permanently deleted.","example":"2025-01-15"}},"required":["date"]},"CreateWorkspaceProviderApiKeyDto":{"type":"object","properties":{"provider":{"type":"string","enum":["cursorCloud","claudeManaged"]},"apiKey":{"type":"string","description":"Raw provider API key to save securely."},"label":{"type":"string","nullable":true}},"required":["provider","apiKey"]},"CreateWorkspaceAgentSkillDto":{"type":"object","properties":{"name":{"type":"string","description":"Skill slug used in SKILL.md frontmatter. Lowercase letters, numbers, and hyphens only.","example":"invoice-analysis"},"displayTitle":{"type":"string","description":"Human-readable skill title shown in Leadtime."},"description":{"type":"string","description":"Short description used for skill discovery. Maximum 1024 characters."},"body":{"type":"string","description":"Markdown instructions loaded when the skill is activated."}},"required":["name","displayTitle","description","body"]},"CreateBotDto":{"type":"object","properties":{"name":{"type":"string","description":"Bot display name.","example":"Company Manager"},"roleId":{"type":"string","description":"Role id to assign. Defaults to the workspace Bot role."},"connectionProvider":{"type":"string","enum":["selfHosted","claudeManaged","cursorCloud","geminiManaged","geminiAgentRuntime","native"],"description":"Optional hosted-agent connection provider to initialize: selfHosted, cursorCloud, or claudeManaged."}},"required":["name"]},"SetPublicBotStatusDto":{"type":"object","properties":{}},"SetPublicBotConnectionProviderDto":{"type":"object","properties":{}},"UpdatePublicBotAgentSettingsDto":{"type":"object","properties":{}},"SetPublicBotConnectionEnabledDto":{"type":"object","properties":{}},"SavePublicCursorCloudBotSettingsDto":{"type":"object","properties":{}},"ValidatePublicCursorCloudConnectionDto":{"type":"object","properties":{}},"ClaudeManagedMcpOAuthCredentialDto":{"type":"object","properties":{"accessToken":{"type":"string","nullable":true},"refreshToken":{"type":"string","nullable":true},"expiresAt":{"type":"string","nullable":true},"tokenEndpoint":{"type":"string","nullable":true},"tokenEndpointAuthType":{"type":"string","enum":["none","client_secret_basic","client_secret_post"],"nullable":true},"scope":{"type":"string","nullable":true},"clientId":{"type":"string","nullable":true},"clientSecret":{"type":"string","nullable":true}}},"ClaudeManagedMcpServerDto":{"type":"object","properties":{"id":{"type":"string","description":"Stable local id for an existing MCP row."},"name":{"type":"string","description":"Display name for the MCP server. If omitted, Leadtime derives one from the URL."},"url":{"type":"string","description":"Hosted MCP server URL."},"authMode":{"type":"string","enum":["none","bearer","oauth"],"description":"Authentication mode for this MCP server."},"bearerToken":{"type":"string","nullable":true,"description":"Bearer token to store for bearer-token MCP servers."},"credentialConfigured":{"type":"boolean","description":"Whether Leadtime already has a stored credential for this MCP server."},"oauthCredential":{"nullable":true,"description":"OAuth credential details for an OAuth MCP server.","allOf":[{"$ref":"#/components/schemas/ClaudeManagedMcpOAuthCredentialDto"}]}},"required":["url"]},"ClaudeManagedSkillRefDto":{"type":"object","properties":{"type":{"type":"string","enum":["anthropic","custom"]},"skillId":{"type":"string","description":"Anthropic built-in skill id (for example xlsx) or Leadtime workspace skill id for custom skills."},"version":{"type":"string","nullable":true,"description":"Optional Anthropic skill version. Defaults to latest when omitted."}},"required":["type","skillId"]},"ClaudeManagedLeadtimeManagedAgentDto":{"type":"object","properties":{"instructions":{"oneOf":[{"type":"string"},{"type":"object"}],"description":"Standard Leadtime editor field for managed Claude agent instructions. Accepts Markdown, HTML, or ProseMirror IDoc JSON (object or JSON string); Leadtime converts it to internal IDoc before storing and syncing the rendered HTML to Claude.\n\nUse only the nodes and marks documented below for this field.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"},"modelId":{"type":"string","example":"claude-sonnet-4-6","description":"Claude model id from the bot settings model cache/resources refresh, for example claude-sonnet-4-6 or claude-opus-4-8."},"mcpServers":{"description":"Custom hosted MCP servers to attach to this Claude agent.","type":"array","items":{"$ref":"#/components/schemas/ClaudeManagedMcpServerDto"}},"skills":{"description":"Skills attached to this Leadtime-managed Claude agent. Maximum 20. Only supported when agentSource is leadtimeManaged.","type":"array","items":{"$ref":"#/components/schemas/ClaudeManagedSkillRefDto"}}}},"ClaudeManagedGithubRepositoryDto":{"type":"object","properties":{"id":{"type":"number","description":"GitHub repository id."},"name":{"type":"string","description":"Repository short name."},"fullName":{"type":"string","description":"Repository full name, for example owner/repo."},"url":{"type":"string","description":"GitHub web URL."},"cloneUrl":{"type":"string","nullable":true,"description":"Repository clone URL."},"private":{"type":"boolean","description":"Whether the GitHub repository is private."}},"required":["id","name","fullName","url","cloneUrl","private"]},"ClaudeManagedBotConfigDto":{"type":"object","properties":{"agentSource":{"type":"string","enum":["leadtimeManaged","existing"],"description":"Use leadtimeManaged when Leadtime should create and repair the Claude agent, environment, and vault."},"agentId":{"type":"string","nullable":true,"description":"Existing Anthropic agent id when agentSource is existing."},"environmentId":{"type":"string","nullable":true,"description":"Existing Anthropic environment id when agentSource is existing."},"userVaultIds":{"description":"Additional user-managed Claude vault ids to attach to sessions.","type":"array","items":{"type":"string"}},"toolMode":{"type":"string","enum":["basic","full"],"description":"Leadtime MCP tool set exposed to Claude task sessions."},"exposeRawApiCredentialToAgent":{"type":"boolean","description":"Expose a raw Leadtime API credential to the agent for trusted automation."},"customGuidance":{"type":"string","nullable":true,"description":"Extra guidance appended to Leadtime task-session instructions."},"managedAgent":{"description":"Leadtime-managed Claude agent configuration. Put modelId, instructions, and mcpServers here.","allOf":[{"$ref":"#/components/schemas/ClaudeManagedLeadtimeManagedAgentDto"}]},"githubRepositoryDefaults":{"description":"Default GitHub repositories mounted into Claude coding sessions for this bot.","type":"array","items":{"$ref":"#/components/schemas/ClaudeManagedGithubRepositoryDto"}}}},"SavePublicClaudeManagedBotSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Enable or disable this bot connection."},"providerApiKeyId":{"type":"string","nullable":true,"description":"Saved Anthropic provider API key id to use for this bot."},"anthropicApiKey":{"type":"string","nullable":true,"description":"Raw Anthropic API key. Prefer providerApiKeyId when a saved key exists."},"config":{"description":"Claude Managed configuration. Put model changes under config.managedAgent.modelId and GitHub defaults under config.githubRepositoryDefaults.","allOf":[{"$ref":"#/components/schemas/ClaudeManagedBotConfigDto"}]}}},"ValidatePublicClaudeManagedConnectionDto":{"type":"object","properties":{"providerApiKeyId":{"type":"string","nullable":true,"description":"Saved Anthropic provider API key id to validate."},"anthropicApiKey":{"type":"string","nullable":true,"description":"Raw Anthropic API key to validate."}}},"PublicClaudeManagedMcpOAuthConnectUrlDto":{"type":"object","properties":{"server":{"$ref":"#/components/schemas/ClaudeManagedMcpServerDto"}},"required":["server"]},"SaveProjectAgentProviderSettingsDto":{"type":"object","properties":{}},"ValidateProjectAgentProviderSettingsDto":{"type":"object","properties":{}},"ManualPositionCategoryResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique category identifier. A UUID that uniquely identifies this category within the workspace. Use this ID when assigning the category to manual positions or when updating/deleting the category.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Category name. The display name of the category as it appears in the application. This is the name that will be shown when the category is assigned to manual positions in offers and invoices.","example":"Development"}},"required":["id","name"]},"CreateOrUpdateManualPositionCategoryDto":{"type":"object","properties":{"id":{"type":"string","description":"Category identifier (UUID). When provided, updates the existing category with this ID. When omitted or set to `null`, creates a new category. Must be a valid UUID format if provided.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Category name. A descriptive label for organizing manual positions. Examples: \"Development\", \"Consulting\", \"Travel Expenses\", \"External Services\", \"Licenses\". Must be a non-empty string. Category names are case-sensitive and can be used across all projects in the workspace.","example":"Development"}},"required":["name"]},"WorkspaceDetailsResponse":{"type":"object","properties":{"id":{"type":"string","example":"workspace_456","description":"Unique identifier for the workspace. Used to reference the workspace in other API endpoints."},"domain":{"type":"string","example":"company-domain","description":"Workspace domain/subdomain identifier. Used in workspace URLs and API routing."},"companyName":{"type":"string","example":"Acme Corp","description":"Company or organization name associated with the workspace. This is the display name shown throughout the application."},"isBillingLocked":{"type":"boolean","example":false,"description":"Indicates whether the workspace is locked due to past due billing status. When true, certain workspace operations may be restricted until billing issues are resolved."},"attendanceEnabled":{"type":"boolean","example":false,"description":"Indicates whether attendance tracking (clock in/out) is enabled for the workspace AND the current user is an Employee type. This field is false if attendance is disabled or if the current user is not an Employee."},"isHelpdeskEnabled":{"type":"boolean","example":true,"description":"Indicates whether the helpdesk feature is enabled for the workspace. When enabled, the workspace can use ticket-based support workflows."},"features":{"type":"array","description":"Array of all enabled workspace features. Includes both currently active features and features that are still available but not yet activated. Features define what functionality is available in the workspace.","items":{"type":"string","enum":["HELPDESK","API","AGENT"]}},"workspaceLanguage":{"type":"string","example":"en","description":"Default language code for the workspace (e.g., \"en\" for English, \"de\" for German). This setting applies to new users; individual users can override this in their profile settings."},"sprintStartDay":{"type":"number","example":1,"description":"Day of the week when sprints/pipeline periods start. 1 = Monday, 2 = Tuesday, ..., 7 = Sunday. Used for capacity planning and pipeline views."},"sprintPeriodDays":{"type":"number","example":14,"description":"Duration of sprint/pipeline periods in days. Typically 7 (one week) or 14 (two weeks). Defines the time span for capacity planning in the pipeline view."},"mainWorkspaceColor":{"type":"string","enum":["DEFAULT","BLACK","RED","ROSE","MAGENTA","GREEN","BLUE","YELLOW","VIOLET"],"description":"Main accent color theme for the workspace interface. Options include Default, Black, Red, Pink, Magenta, Green, Blue, Yellow, Purple. This color affects navigation elements, buttons, and active states throughout the application."},"logoUrl":{"type":"object","example":"https://app.example.com/api/files/public/logo_123","nullable":true,"description":"Public URL for the workspace logo image used in light mode. This logo appears in the top left corner of the workspace above the navigation. Null if no logo has been uploaded. Recommended format: PNG with transparent background, ideal size 1000x260 pixels."},"logoDarkThemeUrl":{"type":"object","example":"https://app.example.com/api/files/public/logo_dark_456","nullable":true,"description":"Public URL for the workspace logo image used in dark mode. This logo appears when users have dark theme enabled. Null if no dark theme logo has been uploaded. Recommended format: PNG with transparent background, ideal size 1000x260 pixels."},"branding":{"type":"object","description":"Workspace branding settings including custom brand color. The brandColor field (if set) is a HEX color code that matches the company brand and is used for internal projects in various views of the application."},"customIcons":{"description":"Array of custom icons available in the workspace. Custom icons are user-uploaded images (PNG or SVG) that can be used to visually tag tickets, projects, documents, or other entities. Each icon has a unique name (e.g., \":team_sales:\") and an associated image file ID.","type":"array","items":{"type":"string"}}},"required":["id","domain","companyName","isBillingLocked","attendanceEnabled","isHelpdeskEnabled","features","workspaceLanguage","sprintStartDay","sprintPeriodDays","mainWorkspaceColor","logoUrl","logoDarkThemeUrl","branding","customIcons"]},"ObjectStatusDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"sort":{"type":"number"},"createdAt":{"type":"string"}},"required":["id","name","color","sort","createdAt"]},"ObjectCustomFieldInTypeDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string"},"sort":{"type":"number"},"objectTypeId":{"type":"string"},"showInList":{"type":"boolean"},"showInHeader":{"type":"boolean"},"selectOptions":{"type":"array"},"translations":{"type":"array"}},"required":["id","name","description","type","sort","objectTypeId","showInList","showInHeader","selectOptions","translations"]},"ObjectTypeDetailResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"icon":{"type":"object","nullable":true},"sort":{"type":"number"},"createdAt":{"type":"string"},"statuses":{"type":"array","items":{"$ref":"#/components/schemas/ObjectStatusDto"}},"customFields":{"type":"array","items":{"$ref":"#/components/schemas/ObjectCustomFieldInTypeDto"}}},"required":["id","name","slug","icon","sort","createdAt","statuses","customFields"]},"WorkspaceObjectSettingsResponseDto":{"type":"object","properties":{"objectTypes":{"description":"All non-deleted object types with nested statuses and per-type custom field definitions (read-only bundle for forms and integrations).","type":"array","items":{"$ref":"#/components/schemas/ObjectTypeDetailResponseDto"}}},"required":["objectTypes"]},"FileUploadResponse":{"type":"object","properties":{"id":{"type":"string","example":"file_123","description":"Unique file identifier. Use this ID to reference the file in other API endpoints that accept file references (e.g., when setting workspace logos, user avatars, or custom icons)."},"filename":{"type":"string","example":"logo.png","description":"Original filename of the uploaded file as provided by the client. This is preserved for reference but may differ from the stored filename."},"mimetype":{"type":"string","example":"image/png","description":"MIME type of the uploaded file (e.g., \"image/png\", \"image/jpeg\", \"image/svg+xml\"). Indicates the file format and how it should be processed or displayed."},"size":{"type":"number","example":1024,"description":"File size in bytes. Use this to validate file size limits or display file size information to users."},"url":{"type":"string","example":"https://app.example.com/api/files/public/file_123","description":"Public URL that can be used to access the file directly. This URL can be embedded in HTML, shared, or used in API responses. The file is accessible without authentication through this URL."}},"required":["id","filename","mimetype","size","url"]},"WorkspaceUserDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the user. Use this ID to reference the user in other API endpoints or when assigning tasks, projects, or other entities to users."},"name":{"type":"string","description":"Full display name of the user, constructed from firstName and lastName. This is the name shown in user lists, dropdowns, and throughout the application interface."},"avatarId":{"type":"object","description":"File ID of the user avatar image. Use this ID with the file API to retrieve the avatar image URL. Null if no avatar has been uploaded for this user."},"position":{"type":"string","description":"Job title or position of the user within the workspace or organization. This field may be empty for users who do not have a position set."},"employeeId":{"type":"object","description":"Employee ID associated with this user, if the user is an Employee type. This links the user to their employee record which contains additional employee-specific information. Null if the user is not an Employee or does not have an associated employee record."},"userType":{"type":"string","enum":["Employee","OrganizationMember","Bot","LeadtimeSupport","HelpdeskUser"],"description":"Type of user account. Options: Employee (internal workspace employee), OrganizationMember (external organization member), LeadtimeSupport (Leadtime support staff). The user type determines what features and data the user can access."},"canLogin":{"type":"boolean","description":"Indicates whether the user account is active and can log in to the workspace. False if the user account is inactive, suspended, or otherwise unable to authenticate."},"isDeleted":{"type":"boolean","description":"Indicates whether the user account has been soft-deleted. Deleted users are typically hidden from active user lists but may still be included in responses depending on the includeDeleted query parameter."},"botSystemKey":{"type":"object","description":"System bot provider key for bot users, such as cursorCloud or claudeManaged. Null for human users and generic self-hosted bots.","nullable":true}},"required":["id","name","position","userType","canLogin","isDeleted"]},"QuickSearchResultDto":{"type":"object","properties":{"title":{"type":"string","description":"Display title or name of the entity. This is the primary text shown in search results and is what the search term matched against.","example":"Project Alpha"},"type":{"type":"string","description":"Type of entity that was found. Indicates which kind of workspace resource this result represents. Use this to determine how to handle or display the result in your application.","enum":["employee","organization","organizationMember","workspace","project","task","team","contact"],"example":"project"},"meta":{"type":"object","description":"Entity metadata object containing the entity ID and additional type-specific fields. The id field is always present and can be used to fetch full entity details. Other fields vary by entity type (e.g., projectId for tasks, organizationId for projects).","example":{"id":"uuid","projectId":"uuid","organizationId":"uuid"}},"color":{"type":"string","description":"Color associated with the entity, typically in HEX format (e.g., \"#ff0000\"). Used for visual identification in lists, kanban boards, and other UI components. May be null if the entity type does not support colors.","example":"#ff0000"},"icon":{"type":"object","description":"Icon identifier for the entity, typically a FontAwesome icon class name (e.g., \"faSquare\", \"faUser\"). Used for visual representation in UI components. May be null if no icon is set for the entity.","example":"faSquare","nullable":true},"shortName":{"type":"object","description":"Short name or code for the entity, often used for quick identification (e.g., \"ORG-001\", \"PRJ-123\"). This is a human-readable identifier that may be displayed alongside the title. May be null if the entity type does not use short names.","example":"ORG-001","nullable":true}},"required":["title","type","meta"]},"ProductListItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000","description":"Unique identifier for the product. Use this ID to reference the product in other API endpoints or when adding the product to projects or tasks."},"name":{"type":"string","example":"Premium Software License","description":"Display name of the product. This is the name shown in product lists, dropdowns, and when the product is added to projects or tasks."},"categoryId":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440001","description":"Identifier of the product category this product belongs to. Categories help organize products in the catalog and can be used for filtering or grouping."},"logoId":{"type":"object","example":"550e8400-e29b-41d4-a716-446655440002","nullable":true,"description":"File ID of the product logo image. Use this ID with the file API to retrieve the logo image URL. Null if no logo has been uploaded for this product."},"priceFixed":{"type":"object","example":5000,"nullable":true,"description":"Fixed one-time price for the product, typically in the smallest currency unit (e.g., cents for USD). Null if this product does not have a fixed price. Products can have multiple pricing types (fixed, subscription, per-unit) or just one."},"priceSubscription":{"type":"object","example":25,"nullable":true,"description":"Recurring subscription price for the product, typically in the smallest currency unit per billing period (e.g., cents for USD). Null if this product does not have subscription pricing. Used for products that are billed on a recurring basis."},"pricePerUnit":{"type":"object","example":150,"nullable":true,"description":"Per-unit price for the product, typically in the smallest currency unit (e.g., cents for USD). Null if this product does not have per-unit pricing. Used when the product price depends on quantity."}},"required":["id","name","categoryId","logoId","priceFixed","priceSubscription","pricePerUnit"]},"WorkspaceFormTemplatesResponseDto":{"type":"object","properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/FormCategoryResponseDto"}},"templates":{"type":"array","items":{"$ref":"#/components/schemas/FormTemplateListItemResponseDto"}}},"required":["categories","templates"]},"DocumentTemplateApiResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the document template (UUID)","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"The name/title of the document template","example":"Project Contract Template"},"description":{"type":"string","description":"A brief description explaining the purpose or use case of this template","example":"Standard contract template for project agreements"},"language":{"type":"string","description":"ISO 639-1 language code for the template (e.g., \"en\", \"de\")","example":"en"},"type":{"type":"string","description":"The type of document this template generates","enum":["ProjectDocument"],"example":"ProjectDocument"},"content":{"type":"string","description":"Template content as HTML. The content includes static text, formatting, and variable placeholders. Variables are represented as HTML spans: `<span data-type=\"variable\" data-id=\"project.name\">...</span>`. The content may also include conditional sections and page breaks.","example":"<h1>Project Contract</h1><p>Project: <span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span></p>"},"workspaceId":{"type":"string","description":"The workspace ID this template belongs to","example":"123e4567-e89b-12d3-a456-426614174000"},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp when the template was created","example":"2024-01-15T10:30:00Z"},"updatedAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp when the template was last updated","example":"2024-01-20T14:45:00Z"},"createdBy":{"type":"string","description":"User ID of the user who created this template","example":"123e4567-e89b-12d3-a456-426614174000"},"updatedBy":{"type":"object","description":"User ID of the user who last updated this template (null if never updated)","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"documentCustomVariables":{"description":"Array of custom variables defined for this template. Custom variables extend beyond standard system variables and allow template-specific placeholders. Each variable includes its type, default value (if any), options (for Select/MultiSelect), and whether it is required when generating documents from this template.","example":[{"id":"var-123","name":"client_name","description":"Client company name","type":"Text","value":"Acme Corp","options":[],"required":true}],"type":"array","items":{"type":"object"}}},"required":["id","name","description","language","type","content","workspaceId","createdAt","updatedAt","createdBy","updatedBy","documentCustomVariables"]},"DocumentCustomVariableDto":{"type":"object","properties":{"id":{"type":"string","description":"Variable ID (UUID). Only required when updating an existing variable. Omit when creating a new variable.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Variable name/key used to reference this variable in the template content. Use lowercase with underscores (e.g., \"client_name\", \"project_budget\"). This name is used in the variable reference: custom.variable_name.","example":"client_name"},"description":{"type":"string","description":"Human-readable label/description for this variable. This is displayed to users when they are prompted to fill in the variable value when generating a document.","example":"Client Company Name"},"type":{"type":"string","description":"The data type of this variable. Determines what kind of input is expected and how the value is validated. Available types: Text (short text), LongText (multi-line text), Number (numeric value), Date (date/time), Boolean (true/false), Select (single choice from options), MultiSelect (multiple choices from options).","enum":["Text","LongText","Number","Date","Boolean","Select","MultiSelect"],"example":"Text"},"value":{"description":"Default value for this variable. The type of value depends on the variable type: - Text/LongText/Select: string\n- Number: number\n- Boolean: boolean\n- Date: ISO 8601 date string (e.g., \"2024-01-15\")\n- MultiSelect: array of strings\n\nThis value is pre-filled when generating a document, but users can override it.","example":"Acme Corp","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}}]},"options":{"description":"Available options for Select or MultiSelect variable types. For Select, users choose one option. For MultiSelect, users can choose multiple options. This field is ignored for other variable types.","example":["Option 1","Option 2","Option 3"],"type":"array","items":{"type":"string"}},"required":{"type":"boolean","description":"Whether this variable must be filled in when generating a document from the template. If true, users cannot proceed without providing a value. If false, the variable is optional.","example":false,"default":false}},"required":["name","type"]},"CreateDocumentTemplateDto":{"type":"object","properties":{"name":{"type":"string","description":"The name/title of the document template. This is displayed in the templates list and when selecting a template.","example":"Project Contract Template"},"description":{"type":"string","description":"A brief description explaining the purpose or use case of this template. Helps users understand when to use this template.","example":"Standard contract template for project agreements with custom payment terms"},"language":{"type":"string","description":"ISO 639-1 language code for the template (e.g., \"en\" for English, \"de\" for German). Determines which language version of the template is used.","example":"en"},"type":{"type":"string","description":"The type of document this template generates. Currently only \"ProjectDocument\" is available, which is used for generating project-related documents.","enum":["ProjectDocument"],"example":"ProjectDocument"},"content":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your document templates. Variables are represented as HTML spans:\n\n**workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`\n**company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`\n**organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`\n**project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`\n**currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`\n**recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`\n**macros**: `macros.todaysDate`, `macros.dearCustomer`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span>`, `<span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>`","example":"<h1>Project Overview</h1><p>Created by <span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span> for <span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>.</p>"},"customVariables":{"description":"Custom variables define template-specific placeholders that extend beyond standard system variables. Each variable can be referenced in the template content using `<span data-type=\"variable\" data-id=\"custom.variable_name\">...</span>` syntax. When generating a document from this template, users will be prompted to fill in these values. Variables support different types (Text, Number, Date, Boolean, Select, MultiSelect) and can be marked as required.","example":[{"name":"client_name","description":"Client company name","type":"Text","value":"Acme Corporation","required":true},{"name":"project_budget","description":"Project budget in USD","type":"Number","value":50000,"required":false}],"type":"array","items":{"$ref":"#/components/schemas/DocumentCustomVariableDto"}}},"required":["name","description","language","type","content"]},"UpdateDocumentTemplateDto":{"type":"object","properties":{"name":{"type":"string","description":"The name/title of the document template. This is displayed in the templates list and when selecting a template.","example":"Project Contract Template"},"description":{"type":"string","description":"A brief description explaining the purpose or use case of this template. Helps users understand when to use this template.","example":"Standard contract template for project agreements with custom payment terms"},"language":{"type":"string","description":"ISO 639-1 language code for the template (e.g., \"en\" for English, \"de\" for German). Determines which language version of the template is used.","example":"en"},"type":{"type":"string","description":"The type of document this template generates. Currently only \"ProjectDocument\" is available, which is used for generating project-related documents.","enum":["ProjectDocument"],"example":"ProjectDocument"},"content":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your document templates. Variables are represented as HTML spans:\n\n**workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`\n**company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`\n**organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`\n**project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`\n**currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`\n**recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`\n**macros**: `macros.todaysDate`, `macros.dearCustomer`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span>`, `<span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>`","example":"<h1>Project Overview</h1><p>Created by <span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span> for <span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>.</p>"},"customVariables":{"description":"Custom variables define template-specific placeholders that extend beyond standard system variables. Each variable can be referenced in the template content using `<span data-type=\"variable\" data-id=\"custom.variable_name\">...</span>` syntax. When generating a document from this template, users will be prompted to fill in these values. Variables support different types (Text, Number, Date, Boolean, Select, MultiSelect) and can be marked as required.","example":[{"name":"client_name","description":"Client company name","type":"Text","value":"Acme Corporation","required":true},{"name":"project_budget","description":"Project budget in USD","type":"Number","value":50000,"required":false}],"type":"array","items":{"$ref":"#/components/schemas/DocumentCustomVariableDto"}}},"required":["name","description","language","type","content"]},"PatchDocumentTemplateDto":{"type":"object","properties":{"content":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your document templates. Variables are represented as HTML spans:\n\n**workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`\n**company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`\n**organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`\n**project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`\n**currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`\n**recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`\n**macros**: `macros.todaysDate`, `macros.dearCustomer`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span>`, `<span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>`","example":"<h1>Project Overview</h1><p>Created by <span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span> for <span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>.</p>"},"customVariables":{"description":"Custom variables define template-specific placeholders that extend beyond standard system variables. Each variable can be referenced in the template content using `<span data-type=\"variable\" data-id=\"custom.variable_name\">...</span>` syntax. When generating a document from this template, users will be prompted to fill in these values. Variables support different types (Text, Number, Date, Boolean, Select, MultiSelect) and can be marked as required. If provided, the entire custom variables array replaces the existing one.","example":[{"name":"client_name","description":"Client company name","type":"Text","value":"Acme Corporation","required":true},{"name":"project_budget","description":"Project budget in USD","type":"Number","value":50000,"required":false}],"type":"array","items":{"$ref":"#/components/schemas/DocumentCustomVariableDto"}}},"required":["content"]},"AutomationApiResponse":{"type":"object","properties":{"id":{"type":"string","description":"Automation ID"},"workspaceId":{"type":"string","description":"Workspace ID"},"title":{"type":"string","description":"Automation title"},"executionType":{"type":"string","description":"Execution type","enum":["agent","workflow"]},"userPrompt":{"type":"object","description":"User prompt for agent (HTML, automation prompt format: basic nodes, mentions, images; no files or videos)","nullable":true},"executionConfig":{"type":"object","description":"Execution config"},"scope":{"type":"string","description":"Scope","enum":["personal","workspace"]},"ownerUserId":{"type":"object","description":"Owner user ID (for personal scope)","nullable":true},"executorUserId":{"type":"string","description":"Executor user ID"},"isEnabled":{"type":"boolean","description":"Whether automation is enabled"},"createdAt":{"format":"date-time","type":"string","description":"Creation timestamp"},"updatedAt":{"format":"date-time","type":"string","description":"Last update timestamp"},"createdBy":{"type":"string","description":"Creator user ID"},"updatedBy":{"type":"object","description":"Last updater user ID","nullable":true},"triggers":{"type":"array","description":"Triggers. For type webhook, webhookSecret is present after save; use POST {base}/api/automations/webhooks/{webhookSecret} to run. For type slackMessageInPublicChannel, definition contains channelId (Slack C…) and optional textPattern/useRegex.","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["daily","weekly","monthly","customCron","webhook","organizationCreated","projectCreated","taskCreated","objectCreated","slackMessageInPublicChannel"]},"isEnabled":{"type":"boolean"},"sortOrder":{"type":"number"},"definition":{"type":"object"},"webhookSecret":{"type":"string","nullable":true,"description":"Only for webhook triggers. Present after save. Inbound webhook: POST {base}/api/automations/webhooks/{webhookSecret} with Content-Type: application/json. No auth; secret authenticates. Request body is passed to the agent as context."}}}}},"required":["id","workspaceId","title","executionType","userPrompt","executionConfig","scope","ownerUserId","executorUserId","isEnabled","createdAt","updatedAt","createdBy","updatedBy","triggers"]},"AutomationTriggerDto":{"type":"object","properties":{"type":{"type":"string","description":"Trigger variant: daily, weekly, monthly, customCron, webhook, organizationCreated, projectCreated, taskCreated, objectCreated, or slackMessageInPublicChannel. slackMessageInPublicChannel fires on new messages in a monitored **public** Slack channel; the workspace must have Slack connected and the bot must be in that channel.","enum":["daily","weekly","monthly","customCron","webhook","organizationCreated","projectCreated","taskCreated","objectCreated","slackMessageInPublicChannel"],"example":"daily"},"isEnabled":{"type":"boolean","description":"Whether the trigger is enabled","example":true},"sortOrder":{"type":"number","description":"Sort order for multiple triggers","example":0},"definition":{"type":"object","description":"Per-trigger settings. **slackMessageInPublicChannel** — required `channelId`: Slack **public channel ID** (`C…`), not the human-readable name. Optional `textPattern` (message must contain this substring, case-insensitive) and `useRegex` (if true, `textPattern` is a JavaScript regex). Example: `{\"channelId\":\"C012ABCDE\",\"textPattern\":\"bug\",\"useRegex\":false}`. On run, the agent receives `triggerEventPayload.slack`: `channelId`, `channelType`, `userId`, `text`, `ts`, `threadTs` — use with Slack integration tools to reply in-thread (`threadTs ?? ts`) and add/remove reactions on `ts`. **organizationCreated / projectCreated / taskCreated / objectCreated** — optional `filters` (FilterStripModel). Field names and `value` shapes must match the **list grid** for that entity: use `action-details` on **`GET /tasks/grid`** for `taskCreated`, **`GET /projects`** for `projectCreated`, **`GET /organizations`** for `organizationCreated`, internal **`GET …/objects/grid`** for `objectCreated` (not interchangeable). For event triggers, `definition.filters` must be a full FilterStripModel object such as `{\"quickSearch\":\"\",\"filters\":[...]}`. **Schedules** — timeOfDay (HH:mm), weekday (0-6), dayOfMonth (1-31), cronExpression (customCron). **webhook** — no required fields.","example":{"channelId":"C012ABCDE","textPattern":"bug","useRegex":false}}},"required":["type"]},"CreateAutomationDto":{"type":"object","properties":{"title":{"type":"string","description":"Automation title","example":"Daily standup reminder"},"executionType":{"type":"string","description":"Execution type (agent or workflow). Currently only agent is supported.","enum":["agent","workflow"],"example":"agent"},"userPrompt":{"type":"object","description":"User prompt for agent (HTML or Markdown). Automation prompt format: basic nodes, mentions, images; no files or videos.","example":"<p>Summarize yesterday progress</p>","nullable":true},"executionConfig":{"type":"object","description":"Execution config (agent-specific or workflow metadata). Not used for trigger matching filters; event trigger filters belong in `triggers[].definition.filters`.","example":{}},"scope":{"type":"string","description":"Scope (personal or workspace). Personal requires managePersonalAutomations; workspace requires manageWorkspaceAutomations.","enum":["personal","workspace"],"example":"personal"},"executorUserId":{"type":"string","description":"User ID of the executor (current user or a bot)","example":"123e4567-e89b-12d3-a456-426614174000"},"isEnabled":{"type":"boolean","description":"Whether the automation is enabled","example":true},"triggers":{"description":"Triggers (daily, weekly, monthly, customCron, webhook, organizationCreated, projectCreated, taskCreated, objectCreated, slackMessageInPublicChannel). At least one required. For webhook, POST JSON to {WORKC_URL}/api/automations/webhooks/{webhookSecret} (no auth). For slackMessageInPublicChannel, see definition.channelId (Slack public channel ID) and optional text filters in AutomationTriggerDto. For event triggers, filters belong inside `triggers[].definition.filters`, not `executionConfig`.","type":"array","items":{"$ref":"#/components/schemas/AutomationTriggerDto"}}},"required":["title","executionType","userPrompt","executionConfig","scope","executorUserId","triggers"]},"UpdateAutomationDto":{"type":"object","properties":{"title":{"type":"string","description":"Automation title","example":"Daily standup reminder"},"executionType":{"type":"string","description":"Execution type (agent or workflow). Currently only agent is supported.","enum":["agent","workflow"],"example":"agent"},"userPrompt":{"type":"object","description":"User prompt for agent (HTML or Markdown). Automation prompt format: basic nodes, mentions, images; no files or videos.","example":"<p>Summarize yesterday progress</p>","nullable":true},"executionConfig":{"type":"object","description":"Execution config (agent-specific or workflow metadata). Not used for trigger matching filters; event trigger filters belong in `triggers[].definition.filters`.","example":{}},"scope":{"type":"string","description":"Scope (personal or workspace). Personal requires managePersonalAutomations; workspace requires manageWorkspaceAutomations.","enum":["personal","workspace"],"example":"personal"},"executorUserId":{"type":"string","description":"User ID of the executor (current user or a bot)","example":"123e4567-e89b-12d3-a456-426614174000"},"isEnabled":{"type":"boolean","description":"Whether the automation is enabled","example":true},"triggers":{"description":"Triggers (daily, weekly, monthly, customCron, webhook, organizationCreated, projectCreated, taskCreated, objectCreated, slackMessageInPublicChannel). At least one required. For webhook, POST JSON to {WORKC_URL}/api/automations/webhooks/{webhookSecret} (no auth). For slackMessageInPublicChannel, see definition.channelId (Slack public channel ID) and optional text filters in AutomationTriggerDto. For event triggers, filters belong inside `triggers[].definition.filters`, not `executionConfig`.","type":"array","items":{"$ref":"#/components/schemas/AutomationTriggerDto"}}},"required":["title","executionType","userPrompt","executionConfig","scope","executorUserId","triggers"]},"TestRunAutomationDto":{"type":"object","properties":{}},"AutomationRunCreatedResponse":{"type":"object","properties":{"id":{"type":"string","description":"Created run ID"},"automationId":{"type":"string","description":"Automation ID"},"status":{"type":"string","description":"Run status (queued)"}},"required":["id","automationId","status"]},"AutomationRunApiResponse":{"type":"object","properties":{"id":{"type":"string","description":"Run ID"},"automationId":{"type":"string","description":"Automation ID"},"cause":{"type":"string","description":"Run cause (triggerFired, testRun)"},"status":{"type":"string","description":"Run status (queued, running, done, failed, canceled)"},"executionType":{"type":"string","description":"Execution type"},"executorUserId":{"type":"string","description":"Executor user ID"},"requestedByUserId":{"type":"object","description":"Requested by user ID","nullable":true},"agentRunId":{"type":"object","description":"Agent run ID when linked","nullable":true},"failureKind":{"type":"object","description":"Failure classification (technical or agentReported) when status is failed","nullable":true},"resultSummary":{"type":"object","description":"Outcome summary reported by the agent","nullable":true},"resultPayload":{"type":"object","description":"Structured result payload from the agent","nullable":true},"testContextDoc":{"type":"object","description":"Optional test context snapshot used for this run","nullable":true},"firedAt":{"format":"date-time","type":"string","description":"When the run was fired"},"startedAt":{"type":"object","description":"When execution started","nullable":true},"finishedAt":{"type":"object","description":"When execution finished","nullable":true},"metadata":{"type":"object","description":"Run metadata"},"messages":{"type":"array","description":"Agent transcript messages when agentRunId is linked (id, role, parts)","items":{"type":"object"}}},"required":["id","automationId","cause","status","executionType","executorUserId","requestedByUserId","agentRunId","failureKind","resultSummary","resultPayload","testContextDoc","firedAt","startedAt","finishedAt","metadata"]},"IProductVariantApiResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the variant"},"name":{"type":"string","description":"Name of the variant (e.g., \"Standard\", \"Pro\", \"Enterprise\")"},"descriptionHtml":{"type":"object","description":"Variant description converted to HTML format. Null if no description is set.","nullable":true},"priceSubscription":{"type":"number","description":"Recurring subscription price for this variant"},"priceFixed":{"type":"number","description":"One-time fixed price for this variant"},"pricePerUnit":{"type":"number","description":"Price per unit for this variant"},"isActivated":{"type":"boolean","description":"Whether this variant is currently activated and available for selection"}},"required":["id","name","descriptionHtml","priceSubscription","priceFixed","pricePerUnit","isActivated"]},"IProductApiResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the product"},"name":{"type":"string","description":"Name of the product or service"},"descriptionHtml":{"type":"string","description":"Product description converted to HTML format for easy display"},"categoryId":{"type":"string","description":"UUID of the product category this product belongs to"},"logoId":{"type":"object","description":"File ID of the product logo. Null if no logo is set.","nullable":true},"logoUrl":{"type":"object","description":"Public URL to access the product logo image. Constructed from logoId if a logo is set. Null if no logo is available.","nullable":true},"priceFixed":{"type":"number","description":"One-time fixed price for the product"},"priceSubscription":{"type":"number","description":"Recurring subscription price for the product"},"pricePerUnit":{"type":"number","description":"Price per unit for quantity-based billing"},"priceUnitName":{"type":"object","description":"Name of the unit for per-unit pricing (e.g., \"user\", \"hour\"). Null if per-unit pricing is not used.","nullable":true},"priceFrequency":{"type":"object","description":"Billing frequency in months (1, 3, 6, or 12). Null if subscription or per-unit pricing is not used.","nullable":true},"variants":{"description":"List of all product variants with their pricing and descriptions","type":"array","items":{"$ref":"#/components/schemas/IProductVariantApiResponse"}},"options":{"description":"List of all product options with their values and configurations. Structure matches ProductOptionApiDto but with additional internal fields.","type":"array","items":{"type":"string"}}},"required":["id","name","descriptionHtml","categoryId","logoId","logoUrl","priceFixed","priceSubscription","pricePerUnit","priceUnitName","priceFrequency","variants","options"]},"ProductVariantApiDto":{"type":"object","properties":{"id":{"type":"string","description":"Variant ID. Include this when updating an existing variant. Omit when creating a new variant."},"name":{"type":"string","description":"Name of the variant (e.g., \"Standard\", \"Pro\", \"Enterprise\"). Used to distinguish different performance levels or configurations of the same product."},"description":{"type":"object","description":"Variant description explaining what this variant includes or offers. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","nullable":true},"priceFixed":{"type":"object","description":"One-time fixed price for this variant. Use this for one-time purchases (e.g., hardware, installation). Can be combined with subscription or per-unit pricing.","nullable":true,"example":999.99},"priceSubscription":{"type":"object","description":"Recurring subscription price for this variant. Use this for recurring revenue models (e.g., monthly SaaS subscription). The billing frequency is set at the product level via priceFrequency.","nullable":true,"example":29.99},"pricePerUnit":{"type":"object","description":"Price per unit for this variant. Use this for flexible billing by quantity (e.g., per user, per hour, per license). Requires priceUnitName to be set at the product level.","nullable":true,"example":5}},"required":["name","description"]},"ProductOptionValueApiDto":{"type":"object","properties":{"id":{"type":"string","description":"Option value ID. Include this when updating an existing option value. Omit when creating a new option value."},"name":{"type":"string","description":"Name of the option value (e.g., \"Add workshop\", \"Enable premium support\", \"Extra 10GB storage\"). This is what customers see when selecting options."},"extraPriceFixed":{"type":"object","description":"Additional one-time fixed price added when this option value is selected. Use this for one-time add-ons or upgrades.","nullable":true,"example":50},"extraPriceSubscription":{"type":"object","description":"Additional recurring subscription price added when this option value is selected. Use this for recurring add-ons (e.g., monthly premium support).","nullable":true,"example":10},"extraPricePerUnit":{"type":"object","description":"Additional per-unit price added when this option value is selected. Use this for quantity-based add-ons.","nullable":true,"example":2.5}},"required":["name"]},"ProductOptionApiDto":{"type":"object","properties":{"id":{"type":"string","description":"Option ID. Include this when updating an existing option. Omit when creating a new option."},"name":{"type":"string","description":"Name of the option group (e.g., \"Additional Services\", \"Storage Options\", \"Support Level\"). This groups related option values together."},"values":{"description":"List of available values for this option. Each value can have its own pricing. At least one value must be provided.","type":"array","items":{"$ref":"#/components/schemas/ProductOptionValueApiDto"}},"isRequired":{"type":"boolean","description":"Whether this option must be selected. If true, customers must choose at least one value from this option before proceeding.","example":false},"isMultiple":{"type":"boolean","description":"Whether customers can select multiple values from this option. If true, customers can select multiple values (e.g., \"Add workshop\" AND \"Enable premium support\"). If false, only one value can be selected.","example":false}},"required":["name","values","isRequired","isMultiple"]},"CreateProductDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.","example":"Website Hosting Package"},"description":{"type":"string","description":"Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"},"categoryId":{"type":"string","description":"UUID of the product category this product belongs to. Categories help organize products into groups (e.g., \"Hardware\", \"Software\", \"Services\").","example":"123e4567-e89b-12d3-a456-426614174000"},"logoId":{"type":"object","description":"File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.","nullable":true,"example":"123e4567-e89b-12d3-a456-426614174000"},"priceFixed":{"type":"object","description":"One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing.","nullable":true,"example":999.99},"priceSubscription":{"type":"object","description":"Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency.","nullable":true,"example":29.99},"pricePerUnit":{"type":"object","description":"Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set.","nullable":true,"example":5},"priceUnitName":{"type":"string","description":"Name of the unit for per-unit pricing (e.g., \"user\", \"hour\", \"license\", \"GB\"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.","example":"user"},"priceFrequency":{"type":"number","description":"Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).","example":1,"enum":[1,3,6,12]},"contractPeriodMonths":{"type":"number","description":"Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.","example":12},"renewalMode":{"type":"string","description":"Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.","enum":["Automatic","Stops"],"example":"Automatic"},"variants":{"description":"List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.","type":"array","items":{"$ref":"#/components/schemas/ProductVariantApiDto"}},"options":{"description":"List of product options. Options are extra services or add-ons that customers can optionally select (e.g., \"Add workshop\", \"Enable premium support\"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.","type":"array","items":{"$ref":"#/components/schemas/ProductOptionApiDto"}}},"required":["name","description","categoryId","variants","options"]},"UpdateProductDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.","example":"Website Hosting Package"},"description":{"type":"string","description":"Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"},"categoryId":{"type":"string","description":"UUID of the product category this product belongs to. Categories help organize products into groups (e.g., \"Hardware\", \"Software\", \"Services\").","example":"123e4567-e89b-12d3-a456-426614174000"},"logoId":{"type":"object","description":"File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.","nullable":true,"example":"123e4567-e89b-12d3-a456-426614174000"},"priceFixed":{"type":"object","description":"One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing.","nullable":true,"example":999.99},"priceSubscription":{"type":"object","description":"Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency.","nullable":true,"example":29.99},"pricePerUnit":{"type":"object","description":"Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set.","nullable":true,"example":5},"priceUnitName":{"type":"string","description":"Name of the unit for per-unit pricing (e.g., \"user\", \"hour\", \"license\", \"GB\"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.","example":"user"},"priceFrequency":{"type":"number","description":"Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).","example":1,"enum":[1,3,6,12]},"contractPeriodMonths":{"type":"number","description":"Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.","example":12},"renewalMode":{"type":"string","description":"Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.","enum":["Automatic","Stops"],"example":"Automatic"},"variants":{"description":"List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.","type":"array","items":{"$ref":"#/components/schemas/ProductVariantApiDto"}},"options":{"description":"List of product options. Options are extra services or add-ons that customers can optionally select (e.g., \"Add workshop\", \"Enable premium support\"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.","type":"array","items":{"$ref":"#/components/schemas/ProductOptionApiDto"}}},"required":["name","description","categoryId","variants","options"]},"PatchProductDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.","example":"Website Hosting Package"},"description":{"type":"string","description":"Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"},"categoryId":{"type":"string","description":"UUID of the product category this product belongs to. Categories help organize products into groups (e.g., \"Hardware\", \"Software\", \"Services\").","example":"123e4567-e89b-12d3-a456-426614174000"},"logoId":{"type":"object","description":"File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.","nullable":true,"example":"123e4567-e89b-12d3-a456-426614174000"},"priceFixed":{"type":"object","description":"One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing.","nullable":true,"example":999.99},"priceSubscription":{"type":"object","description":"Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency.","nullable":true,"example":29.99},"pricePerUnit":{"type":"object","description":"Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set.","nullable":true,"example":5},"priceUnitName":{"type":"string","description":"Name of the unit for per-unit pricing (e.g., \"user\", \"hour\", \"license\", \"GB\"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.","example":"user"},"priceFrequency":{"type":"number","description":"Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).","example":1,"enum":[1,3,6,12]},"contractPeriodMonths":{"type":"number","description":"Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.","example":12},"renewalMode":{"type":"string","description":"Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.","enum":["Automatic","Stops"],"example":"Automatic"},"variants":{"description":"List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.","type":"array","items":{"$ref":"#/components/schemas/ProductVariantApiDto"}},"options":{"description":"List of product options. Options are extra services or add-ons that customers can optionally select (e.g., \"Add workshop\", \"Enable premium support\"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.","type":"array","items":{"$ref":"#/components/schemas/ProductOptionApiDto"}}}},"ProductCategoryTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German). This determines which language interface will display this translation.","example":"en"},"name":{"type":"object","description":"Translated name of the product category in the specified language. This name appears in multilingual user interfaces and documents when the user language matches.","example":"Software"},"description":{"type":"object","description":"Translated description of the product category in the specified language. This description appears in multilingual user interfaces and documents when the user language matches.","example":"Software products and licenses"}},"required":["language"]},"ProductCategoryResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the product category. Use this ID to reference the category in other API calls.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Display name of the product category. This is the primary name used to identify and organize products.","example":"Software"},"icon":{"type":"string","description":"Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns.","example":":package:"},"description":{"type":"object","description":"Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products.","example":"Software products and licenses"},"sort":{"type":"number","description":"Numeric sort order for displaying categories. Lower numbers appear first. Used to control the order in which categories appear in dropdowns and lists.","example":1},"defaultKey":{"type":"object","description":"Default key identifier for system-defined categories (e.g., \"software\", \"hardware\", \"consulting\", \"external_costs\"). Only present for categories that were created from system defaults. Custom categories created by users do not have a defaultKey. This key is used when restoring default categories.","example":"software"},"translations":{"description":"Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference.","example":[{"language":"en","name":"Software","description":"Software products and licenses"},{"language":"de","name":"Software","description":"Softwareprodukte und Lizenzen"}],"type":"array","items":{"$ref":"#/components/schemas/ProductCategoryTranslationDto"}},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp indicating when the product category was created. Format: YYYY-MM-DDTHH:mm:ss.sssZ","example":"2024-01-01T00:00:00.000Z"},"deletedAt":{"type":"object","description":"ISO 8601 timestamp indicating when the product category was soft-deleted, or null if not deleted. Format: YYYY-MM-DDTHH:mm:ss.sssZ. Soft-deleted categories are marked as deleted but not permanently removed.","example":null}},"required":["id","name","icon","translations","createdAt"]},"CreateProductCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name of the product category. This is the primary name used to identify and organize products. Examples: \"Hardware\", \"Software\", \"Consulting\", \"Subscriptions\".","example":"Software"},"icon":{"type":"string","description":"Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns. Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:, :package:. Default is :rocket: if not specified.","example":":package:"},"description":{"type":"object","description":"Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products.","example":"Software products and licenses"},"translations":{"description":"Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference. Common languages: \"en\" (English), \"de\" (German).","example":[{"language":"en","name":"Software","description":"Software products and licenses"},{"language":"de","name":"Software","description":"Softwareprodukte und Lizenzen"}],"type":"array","items":{"$ref":"#/components/schemas/ProductCategoryTranslationDto"}}},"required":["name","icon","translations"]},"UpdateProductCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name of the product category. This is the primary name used to identify and organize products. Examples: \"Hardware\", \"Software\", \"Consulting\", \"Subscriptions\". All fields are required in PUT requests.","example":"Software"},"icon":{"type":"string","description":"Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns. Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:, :package:. All fields are required in PUT requests.","example":":package:"},"description":{"type":"object","description":"Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products. All fields are required in PUT requests.","example":"Software products and licenses"},"translations":{"description":"Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference. Common languages: \"en\" (English), \"de\" (German). All fields are required in PUT requests.","type":"array","items":{"$ref":"#/components/schemas/ProductCategoryTranslationDto"}}}},"PatchProductCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name of the product category. This is the primary name used to identify and organize products. Examples: \"Hardware\", \"Software\", \"Consulting\", \"Subscriptions\". Only include this field if you want to update the name. If not provided, the existing name remains unchanged.","example":"Software"},"icon":{"type":"string","description":"Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns. Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:, :package:. Only include this field if you want to update the icon. If not provided, the existing icon remains unchanged.","example":":package:"},"description":{"type":"object","description":"Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products. Only include this field if you want to update the description. If not provided, the existing description remains unchanged. Set to null to clear the description.","example":"Software products and licenses"},"translations":{"description":"Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference. Common languages: \"en\" (English), \"de\" (German). Only include this field if you want to update translations. If provided, it replaces all existing translations. If not provided, existing translations remain unchanged.","type":"array","items":{"$ref":"#/components/schemas/ProductCategoryTranslationDto"}}}},"SortProductCategoriesDto":{"type":"object","properties":{"ids":{"description":"Array of product category IDs in the desired display order. The first ID in the array will be displayed first, the second ID will be displayed second, and so on. All category IDs must be valid UUIDs that exist in the workspace. The sort order is updated for all categories based on their position in this array.","example":["123e4567-e89b-12d3-a456-426614174000","223e4567-e89b-12d3-a456-426614174001","323e4567-e89b-12d3-a456-426614174002"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"RestoreProductCategoriesDto":{"type":"object","properties":{"keepCustom":{"type":"boolean","description":"Whether to preserve custom (user-created) product categories when restoring default categories. If true, system default categories are restored while keeping any custom categories you created. If false (default), all custom categories are removed and only system defaults are restored. System default categories include: External Costs, Software, Hardware, and Consulting with multilingual translations.","example":false,"default":false}}},"RoleListItem":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the role. Base roles have IDs containing underscore-prefixed identifiers (e.g., \"_root_\", \"_ceo_\", \"_staff_\"). Custom roles have workspace-prefixed IDs.","example":"workspace-id_employee"},"name":{"type":"string","description":"Display name of the role, localized based on user language preference.","example":"Employee"},"description":{"type":"string","description":"Human-readable description of the role purpose and responsibilities, localized based on user language preference.","example":"Standard employee role"},"icon":{"type":"string","description":"Icon identifier for the role. Can be a RemixIcon class name (e.g., \"ri-user-line\") or an emoji code (e.g., \":person_in_tuxedo:\"). Used for visual identification in role lists.","example":"ri-user-line"},"parentId":{"type":"string","description":"ID of the parent role if this role inherits permissions from another role. Undefined if the role has no parent (e.g., base roles or top-level custom roles).","example":"workspace-id_admin"},"type":{"type":"string","description":"Role type indicating the intended use case. \"normal\" for internal team members, \"guest\" for external users. Determines permission restrictions.","example":"normal"},"readOnly":{"type":"boolean","description":"Boolean flag indicating if this is a base role (system-defined) that cannot be modified or deleted. Base roles include Root, CEO, Team Leader, Staff, and Guest.","example":false}},"required":["id","name","description","icon","type","readOnly"]},"PermissionItem":{"type":"object","properties":{"key":{"type":"string","description":"Unique permission identifier using dot notation format \"category.action\" (e.g., \"projects.create\", \"tasks.delete\"). This key is used when assigning permissions to roles.","example":"projects.create"},"group":{"type":"string","description":"Functional group that this permission belongs to. Permissions are organized into groups like \"projects\", \"tasks\", \"employees\", \"roles\", etc. for easier management and UI display.","example":"projects"}},"required":["key","group"]},"RoleDetailsResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the role. Base roles have IDs containing underscore-prefixed identifiers (e.g., \"_root_\", \"_ceo_\", \"_staff_\"). Custom roles have workspace-prefixed IDs.","example":"workspace-id_employee"},"name":{"type":"string","description":"Display name of the role, localized based on user language preference.","example":"Employee"},"description":{"type":"string","description":"Human-readable description of the role purpose and responsibilities, localized based on user language preference.","example":"Standard employee role"},"icon":{"type":"string","description":"Icon identifier for the role. Can be a RemixIcon class name (e.g., \"ri-user-line\") or an emoji code (e.g., \":person_in_tuxedo:\"). Used for visual identification in role lists.","example":"ri-user-line"},"parentId":{"type":"string","description":"ID of the parent role if this role inherits permissions from another role. Undefined if the role has no parent (e.g., base roles or top-level custom roles).","example":"workspace-id_admin"},"type":{"type":"string","description":"Role type indicating the intended use case. \"normal\" for internal team members, \"guest\" for external users. Determines permission restrictions.","example":"normal"},"permissions":{"type":"object","description":"Complete map of all permissions for this role. Keys are permission identifiers (e.g., \"projects.create\", \"tasks.delete\"). Values are true if the permission is explicitly allowed, false if explicitly disallowed, or missing if inherited from parent role. This map includes both explicitly set permissions and inherited permissions.","example":{"projects.create":true,"projects.delete":false}},"readOnly":{"type":"boolean","description":"Boolean flag indicating if this is a base role (system-defined) that cannot be modified or deleted. Base roles include Root, CEO, Team Leader, Staff, and Guest.","example":false},"createdBy":{"type":"string","description":"User ID of the person who created this custom role. Only present for custom roles, not base roles."},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp indicating when this custom role was created. Only present for custom roles, not base roles."},"editedAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp indicating when this custom role was last modified. Only present for custom roles, not base roles."}},"required":["id","name","description","icon","type","permissions","readOnly"]},"CreateRoleDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the role. This is shown in user interfaces when selecting or displaying roles.","example":"Team Lead"},"description":{"type":"string","description":"Human-readable description explaining the role purpose and responsibilities. Maximum 400 characters.","example":"Leads a delivery team"},"icon":{"type":"string","description":"Icon identifier for the role. Can be a RemixIcon class name (e.g., \"ri-user-star-line\") or an emoji code (e.g., \":person_in_tuxedo:\"). Used for visual identification in role lists and user interfaces.","example":"ri-user-star-line"},"parentId":{"type":"string","description":"ID of the parent role for permission inheritance. If provided, this role will inherit all permissions from the parent role. Inherited permissions can be overridden by explicitly setting them in the permissions map. Must not create circular dependencies (cannot set a child role as parent).","example":"workspace-id_employee"},"type":{"type":"string","description":"Role type determines the intended use case and permission restrictions. \"normal\" roles are for internal team members (employees, managers). \"guest\" roles are for external users like clients or partners and have additional permission restrictions.","example":"normal","enum":["normal","guest"]},"permissions":{"type":"object","description":"Map of permission keys to boolean values. Keys are permission identifiers (e.g., \"projects.create\", \"tasks.delete\"). Values are true to allow the permission or false to disallow it. If not provided and a parent role is set, permissions are inherited from the parent. Guest roles cannot have certain permissions (e.g., workspace administration).","example":{"projects.create":true,"projects.delete":false}}},"required":["name","icon","type"]},"UpdateRoleDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the role. This is shown in user interfaces when selecting or displaying roles.","example":"Team Lead"},"description":{"type":"string","description":"Human-readable description explaining the role purpose and responsibilities. Maximum 400 characters.","example":"Leads a delivery team"},"icon":{"type":"string","description":"Icon identifier for the role. Can be a RemixIcon class name (e.g., \"ri-user-star-line\") or an emoji code (e.g., \":person_in_tuxedo:\"). Used for visual identification in role lists and user interfaces.","example":"ri-user-star-line"},"parentId":{"type":"string","description":"ID of the parent role for permission inheritance. If provided, this role will inherit all permissions from the parent role. Inherited permissions can be overridden by explicitly setting them in the permissions map. Must not create circular dependencies (cannot set a child role as parent).","example":"workspace-id_employee"},"type":{"type":"string","description":"Role type determines the intended use case and permission restrictions. \"normal\" roles are for internal team members (employees, managers). \"guest\" roles are for external users like clients or partners and have additional permission restrictions.","example":"normal","enum":["normal","guest"]},"permissions":{"type":"object","description":"Complete map of permission keys to boolean values. Keys are permission identifiers (e.g., \"projects.create\", \"tasks.delete\"). Values are true to allow the permission or false to disallow it. This replaces the entire permission map - all permissions must be specified. Guest roles cannot have certain permissions (e.g., workspace administration).","example":{"projects.create":true,"projects.delete":false}}},"required":["name","icon","type"]},"PatchRoleDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the role. If provided, updates the role name. If not provided, the existing name is kept.","example":"Team Lead"},"description":{"type":"string","description":"Human-readable description explaining the role purpose and responsibilities. Maximum 400 characters. If provided, updates the description. If not provided, the existing description is kept.","example":"Leads a delivery team"},"icon":{"type":"string","description":"Icon identifier for the role. Can be a RemixIcon class name (e.g., \"ri-user-star-line\") or an emoji code (e.g., \":person_in_tuxedo:\"). If provided, updates the icon. If not provided, the existing icon is kept.","example":":person_in_tuxedo:"},"parentId":{"type":"string","description":"ID of the parent role for permission inheritance. If provided, changes the parent role and permissions are recalculated based on the new parent. If not provided, the existing parent is kept. Must not create circular dependencies (cannot set a child role as parent).","example":"workspace-id_employee"},"type":{"type":"string","description":"Role type determines the intended use case and permission restrictions. \"normal\" roles are for internal team members. \"guest\" roles are for external users and have additional permission restrictions. If provided, changes the role type. If not provided, the existing type is kept.","example":"normal","enum":["normal","guest"]}}},"ReplacePermissionsDto":{"type":"object","properties":{"permissions":{"type":"object","description":"Complete map of permission keys to boolean values that replaces the entire permission set for the role. Keys are permission identifiers using dot notation (e.g., \"projects.create\", \"tasks.delete\"). Values are true to allow the permission or false to disallow it. This is a complete replacement - all permissions must be specified. Permissions set to false are removed from the role. Permissions are validated against disallowed permissions for the role type.","example":{"projects.create":true,"projects.delete":false,"projects.edit":true}}},"required":["permissions"]},"TogglePermissionDto":{"type":"object","properties":{"allow":{"type":"boolean","description":"Boolean value indicating whether to grant (true) or revoke (false) the permission specified in the URL path. Setting to true explicitly allows the permission, even if it was inherited from a parent role. Setting to false explicitly disallows the permission, overriding any inheritance.","example":true}},"required":["allow"]},"TaskAutoGenerationSettingsDto":{"type":"object","properties":{"tasksAutoGenerateTitle":{"type":"boolean","description":"Whether to automatically generate task titles","example":true},"tasksAutoGenerateSummary":{"type":"boolean","description":"Whether to automatically generate task summaries","example":true},"tasksAutoGenerateIcon":{"type":"boolean","description":"Whether to automatically generate task icons","example":true}},"required":["tasksAutoGenerateTitle","tasksAutoGenerateSummary","tasksAutoGenerateIcon"]},"CustomFieldTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"Language code following ISO 639-1 format (e.g., \"en\", \"de\", \"fr\")","example":"en"},"name":{"type":"string","description":"Translated name for this language. Overrides the default name when user's language matches.","example":"Priority"}},"required":["language","name"]},"FieldOptionDto":{"type":"object","properties":{"id":{"type":"object","description":"Option ID","example":"1"},"value":{"type":"string","description":"Default option value (used as fallback when no translation exists for user's language)","example":"High"},"translations":{"description":"Language-specific translations for this option. Overrides the default value when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationDto"}}},"required":["value","translations"]},"TaskCustomFieldResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Default custom field name (used as fallback when no translation exists for user's language)","example":"Priority"},"description":{"type":"object","description":"Default custom field description (used as fallback when no translation exists for user's language)","example":"Task priority level","nullable":true},"sort":{"type":"number","description":"Sort order","example":1},"type":{"type":"string","description":"Custom field type","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"entity":{"type":"string","description":"Entity type (always Task for task custom fields)","enum":["Task","Project","Organization","Object"],"example":"Task"},"translations":{"description":"Language-specific translations for field name. Overrides the default name when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationDto"}},"selectOptions":{"description":"Select options with their translations. Each option has a default value and language-specific overrides.","type":"array","items":{"$ref":"#/components/schemas/FieldOptionDto"}}},"required":["id","name","description","sort","type","entity","translations","selectOptions"]},"CreateTaskCustomFieldDto":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID (optional for create)","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Default custom field name (used as fallback when no translation exists for user's language)","example":"Priority"},"description":{"type":"string","description":"Default custom field description (used as fallback when no translation exists for user's language)","example":"Task priority level"},"type":{"type":"string","description":"Custom field type","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"selectOptions":{"description":"Select options (required for Select and MultiSelect types)","type":"array","items":{"$ref":"#/components/schemas/FieldOptionDto"}},"translations":{"description":"Language-specific translations for field name. Overrides the default name when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationDto"}},"translationsDescriptions":{"description":"Language-specific translations for field description. Overrides the default description when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationDto"}}},"required":["name","description","type","selectOptions","translations","translationsDescriptions"]},"UpdateTaskCustomFieldDto":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID (optional, taken from URL)","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Default custom field name (used as fallback when no translation exists for user's language)","example":"Priority"},"description":{"type":"string","description":"Default custom field description (used as fallback when no translation exists for user's language)","example":"Task priority level"},"type":{"type":"string","description":"Custom field type","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"selectOptions":{"description":"Select options (required for Select and MultiSelect types)","type":"array","items":{"$ref":"#/components/schemas/FieldOptionDto"}},"translations":{"description":"Language-specific translations for field name. Overrides the default name when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationDto"}},"translationsDescriptions":{"description":"Language-specific translations for field description. Overrides the default description when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationDto"}}},"required":["name","description","type","selectOptions","translations","translationsDescriptions"]},"SortTaskCustomFieldsDto":{"type":"object","properties":{"ids":{"description":"Array of custom field IDs in the desired order","example":["123e4567-e89b-12d3-a456-426614174000","987fcdeb-51a2-43d7-b456-426614174000"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"RestoreDefaultsDto":{"type":"object","properties":{"keepCustom":{"type":"boolean","description":"Whether to keep custom task types, statuses, and work activities","example":false}},"required":["keepCustom"]},"WorkActivityTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"Language code following ISO 639-1 format (e.g., \"en\", \"de\", \"fr\")","example":"en"},"name":{"type":"string","description":"Translated name for this language. Overrides the default name when user's language matches.","example":"Development"}},"required":["language","name"]},"WorkActivityResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Work activity ID","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Default work activity name (used as fallback when no translation exists for user's language)","example":"Development"},"sort":{"type":"number","description":"Sort order","example":1},"translations":{"description":"Language-specific translations for activity name. Overrides the default name when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/WorkActivityTranslationDto"}}},"required":["id","name","sort","translations"]},"CreateWorkActivityDto":{"type":"object","properties":{"id":{"type":"string","description":"Work activity ID (optional for create)","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Default work activity name (used as fallback when no translation exists for user's language)","example":"Development"},"translations":{"description":"Language-specific translations for activity name. Overrides the default name when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/WorkActivityTranslationDto"}}},"required":["name","translations"]},"UpdateWorkActivityDto":{"type":"object","properties":{"name":{"type":"string","description":"Default work activity name (used as fallback when no translation exists for user's language)","example":"Development"},"translations":{"description":"Language-specific translations for activity name. Overrides the default name when user's language matches.","type":"array","items":{"$ref":"#/components/schemas/WorkActivityTranslationDto"}}},"required":["name","translations"]},"SortWorkActivitiesDto":{"type":"object","properties":{"ids":{"description":"Array of work activity IDs in the desired order","example":["123e4567-e89b-12d3-a456-426614174000","987fcdeb-51a2-43d7-b456-426614174000"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"TaskStatusTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"Language code (e.g., \"en\", \"de\")","example":"en"},"name":{"type":"string","description":"Translated name of the task status","example":"In Progress"}},"required":["language","name"]},"TaskStatusResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the task status","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Name of the task status","example":"In Progress"},"icon":{"type":"string","description":"Icon for the task status in format :icon_name: (e.g., :hourglass_flowing_sand:, :check_mark:, :x:)","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Type of the task status","enum":["New","InProgress","Feedback","Backlog","Resolved","Closed"],"example":"InProgress"},"sort":{"type":"number","description":"Sort order of the task status","example":1},"translations":{"description":"Translations for the task status","example":[{"language":"en","name":"In Progress"},{"language":"de","name":"In Bearbeitung"}],"type":"array","items":{"$ref":"#/components/schemas/TaskStatusTranslationDto"}}},"required":["id","name","icon","type","sort","translations"]},"CreateTaskStatusDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the task status","example":"In Progress"},"icon":{"type":"string","description":"Icon for the task status in format :icon_name: (e.g., :hourglass_flowing_sand:, :check_mark:, :x:)","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Type of the task status","enum":["New","InProgress","Feedback","Backlog","Resolved","Closed"],"example":"InProgress"},"translations":{"description":"Translations for the task status","example":[{"language":"en","name":"In Progress"},{"language":"de","name":"In Bearbeitung"}],"type":"array","items":{"$ref":"#/components/schemas/TaskStatusTranslationDto"}}},"required":["name","icon","type","translations"]},"UpdateTaskStatusDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the task status","example":"In Progress"},"icon":{"type":"string","description":"Icon for the task status in format :icon_name: (e.g., :hourglass_flowing_sand:, :check_mark:, :x:)","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Type of the task status","enum":["New","InProgress","Feedback","Backlog","Resolved","Closed"],"example":"InProgress"},"translations":{"description":"Translations for the task status","example":[{"language":"en","name":"In Progress"},{"language":"de","name":"In Bearbeitung"}],"type":"array","items":{"$ref":"#/components/schemas/TaskStatusTranslationDto"}}}},"SortTaskStatusesDto":{"type":"object","properties":{"ids":{"description":"Array of task status IDs in the desired order","example":["status-1","status-2","status-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"TaskTypeTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"Language code for the translation","example":"en"},"name":{"type":"string","description":"Translated name of the task type","example":"Feature"}},"required":["language","name"]},"TaskTypeResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the task type","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Name of the task type","example":"Feature"},"icon":{"type":"string","description":"Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)","example":":rocket:"},"type":{"type":"string","description":"Type of the task type","enum":["Feature","Bug"],"example":"Feature"},"sort":{"type":"number","description":"Sort order of the task type","example":1},"translations":{"description":"Translations for the task type","example":[{"language":"en","name":"Feature"},{"language":"de","name":"Funktion"}],"type":"array","items":{"$ref":"#/components/schemas/TaskTypeTranslationDto"}},"statuses":{"description":"Array of task status IDs that can be used with this task type","example":["status-1","status-2"],"type":"array","items":{"type":"string"}},"customFields":{"description":"Array of custom field IDs that are available for this task type","example":["field-1","field-2"],"type":"array","items":{"type":"string"}},"templateDoc":{"type":"string","description":"Template document content as HTML","example":"<h1>Task Template</h1><p>This is a template for new tasks of this type.</p>"},"requiredFields":{"description":"Array of required field names for this task type","example":["title","description"],"type":"array","items":{"type":"string"}}},"required":["id","name","icon","type","sort","translations","statuses","customFields","templateDoc","requiredFields"]},"CreateTaskTypeDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the task type","example":"Feature"},"icon":{"type":"string","description":"Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)","example":":rocket:"},"type":{"type":"string","description":"Type of the task type","enum":["Feature","Bug"],"example":"Feature"},"translations":{"description":"Translations for the task type","example":[{"language":"en","name":"Feature"},{"language":"de","name":"Funktion"}],"type":"array","items":{"$ref":"#/components/schemas/TaskTypeTranslationDto"}},"statuses":{"description":"Array of task status IDs that can be used with this task type","example":["status-1","status-2"],"type":"array","items":{"type":"string"}},"customFields":{"description":"Array of custom field IDs that are available for this task type","example":["field-1","field-2"],"type":"array","items":{"type":"string"}},"templateDoc":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<h1>Task Template</h1><p>This is a template for new tasks of this type.</p>"},"requiredFields":{"description":"Array of required field names for this task type","example":["title","description"],"type":"array","items":{"type":"string"}}},"required":["name","icon","type","translations","statuses","customFields","templateDoc","requiredFields"]},"UpdateTaskTypeDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the task type","example":"Feature"},"icon":{"type":"string","description":"Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)","example":":rocket:"},"type":{"type":"string","description":"Type of the task type","enum":["Feature","Bug"],"example":"Feature"},"translations":{"description":"Translations for the task type","example":[{"language":"en","name":"Feature"},{"language":"de","name":"Funktion"}],"type":"array","items":{"$ref":"#/components/schemas/TaskTypeTranslationDto"}},"statuses":{"description":"Array of task status IDs that can be used with this task type","example":["status-1","status-2"],"type":"array","items":{"type":"string"}},"customFields":{"description":"Array of custom field IDs that are available for this task type","example":["field-1","field-2"],"type":"array","items":{"type":"string"}},"templateDoc":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<h1>Task Template</h1><p>This is a template for new tasks of this type.</p>"},"requiredFields":{"description":"Array of required field names for this task type","example":["title","description"],"type":"array","items":{"type":"string"}}}},"PatchTaskTypeDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the task type","example":"Feature"},"icon":{"type":"string","description":"Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)","example":":rocket:"},"type":{"type":"string","description":"Type of the task type","enum":["Feature","Bug"],"example":"Feature"},"translations":{"description":"Translations for the task type","example":[{"language":"en","name":"Feature"},{"language":"de","name":"Funktion"}],"type":"array","items":{"$ref":"#/components/schemas/TaskTypeTranslationDto"}},"statuses":{"description":"Array of task status IDs that can be used with this task type","example":["status-1","status-2"],"type":"array","items":{"type":"string"}},"customFields":{"description":"Array of custom field IDs that are available for this task type","example":["field-1","field-2"],"type":"array","items":{"type":"string"}},"templateDoc":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<h1>Task Template</h1><p>This is a template for new tasks of this type.</p>"},"requiredFields":{"description":"Array of required field names for this task type","example":["title","description"],"type":"array","items":{"type":"string"}}}},"SortTaskTypesDto":{"type":"object","properties":{"ids":{"description":"Array of task type IDs in the desired sort order","example":["type-1","type-2","type-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"ObjectCustomFieldResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"sort":{"type":"number"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"]},"entity":{"type":"string","enum":["Task","Project","Organization","Object"]},"objectTypeId":{"type":"string"},"showInList":{"type":"boolean"},"showInHeader":{"type":"boolean"},"translations":{"type":"array"},"selectOptions":{"type":"array"}},"required":["id","name","description","sort","type","entity","objectTypeId","showInList","showInHeader","translations","selectOptions"]},"SaveSortDTO":{"type":"object","properties":{}},"CustomFieldTranslationPublicDto":{"type":"object","properties":{"language":{"type":"string"},"name":{"type":"string"}},"required":["language","name"]},"FieldOptionPublicDto":{"type":"object","properties":{"id":{"type":"object"},"value":{"type":"string"},"translations":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationPublicDto"}}},"required":["id","value","translations"]},"CreateObjectCustomFieldDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"]},"objectTypeId":{"type":"string","description":"Must be the object type this field belongs to"},"showInList":{"type":"boolean"},"showInHeader":{"type":"boolean"},"selectOptions":{"type":"array","items":{"$ref":"#/components/schemas/FieldOptionPublicDto"}},"translations":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationPublicDto"}},"translationsDescriptions":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationPublicDto"}}},"required":["name","type","objectTypeId","translations","translationsDescriptions"]},"UpdateObjectCustomFieldDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"]},"objectTypeId":{"type":"string"},"showInList":{"type":"boolean"},"showInHeader":{"type":"boolean"},"selectOptions":{"type":"array","items":{"$ref":"#/components/schemas/FieldOptionPublicDto"}},"translations":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationPublicDto"}},"translationsDescriptions":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldTranslationPublicDto"}}},"required":["name","type","objectTypeId","translations","translationsDescriptions"]},"ProjectCategoryTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"object","description":"Translated name of the project category in this language. Used for multilingual display in the user interface.","example":"Development"},"description":{"type":"object","description":"Translated description of the project category in this language. Provides context about when to use this category.","example":"Software development projects"}},"required":["language"]},"ProjectCategoryResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the project category. Used to reference this category when creating or updating projects.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Display name of the project category. Shown in dropdowns and project lists.","example":"Development"},"icon":{"type":"string","description":"Icon identifier in emoji format (e.g., \":rocket:\"). Displayed next to the category name throughout the application.","example":":rocket:"},"description":{"type":"object","description":"Optional description providing context about when to use this category.","example":"Projects related to software development"},"sort":{"type":"number","description":"Numeric sort order for display purposes. Lower numbers appear first in lists and dropdowns. May be undefined if not explicitly set.","example":1},"translations":{"description":"Complete array of translations for all supported languages. Each translation contains the language code and localized name/description.","example":[{"language":"en","name":"Development","description":"Software development projects"},{"language":"de","name":"Entwicklung","description":"Softwareentwicklungsprojekte"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectCategoryTranslationDto"}}},"required":["id","name","icon","translations"]},"CreateProjectCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the project category. Used to organize projects by type or purpose (e.g., \"Development\", \"Support\", \"New Client Project\").","example":"Development"},"icon":{"type":"string","description":"Icon identifier in emoji format. Must be wrapped in colons (e.g., \":rocket:\", \":wrench:\", \":briefcase:\"). The icon appears next to the category name throughout the application.","example":":rocket:"},"description":{"type":"object","description":"Optional description providing context about when to use this category. Helps users understand the purpose of the category.","example":"Projects related to software development"},"translations":{"description":"Array of translations for multilingual support. Each translation object contains a language code and translated name/description. The system automatically displays the correct language based on user preferences.","example":[{"language":"en","name":"Development","description":"Software development projects"},{"language":"de","name":"Entwicklung","description":"Softwareentwicklungsprojekte"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectCategoryTranslationDto"}}},"required":["name","icon","translations"]},"UpdateProjectCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project category","example":"Development"},"icon":{"type":"string","description":"Icon for the project category in format :icon_name:","example":":rocket:"},"description":{"type":"object","description":"Description of the project category","example":"Projects related to software development"},"translations":{"description":"Translations for the project category","type":"array","items":{"$ref":"#/components/schemas/ProjectCategoryTranslationDto"}}}},"PatchProjectCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project category","example":"Development"},"icon":{"type":"string","description":"Icon for the project category in format :icon_name:","example":":rocket:"},"description":{"type":"object","description":"Description of the project category","example":"Projects related to software development"},"translations":{"description":"Translations for the project category","type":"array","items":{"$ref":"#/components/schemas/ProjectCategoryTranslationDto"}}}},"SortProjectCategoriesDto":{"type":"object","properties":{"ids":{"description":"Array of project category IDs in the desired order","example":["category-1","category-2","category-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"ProjectStatusTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"object","description":"Translated name of the project status in this language. Used for multilingual display in the user interface.","example":"In Progress"},"description":{"type":"object","description":"Translated description of the project status in this language. Provides context about when to use this status.","example":"Project is currently in progress"}},"required":["language"]},"ProjectStatusResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the project status. Used to reference this status when creating or updating projects.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Display name of the project status. Shown in dropdowns and project lists.","example":"In Progress"},"icon":{"type":"string","description":"Icon identifier in emoji format (e.g., \":hourglass_flowing_sand:\"). Displayed next to the status name throughout the application.","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Status type enum value that categorizes the status. Determines billing eligibility and workflow behavior. Valid values: Sales, Requirements, Implementation, QualityManagement, Billing, Done.","enum":["Sales","Requirements","Implementation","QualityManagement","Billing","Done"],"example":"Implementation"},"description":{"type":"object","description":"Optional description providing context about when to use this status.","example":"Project is currently in development phase"},"sort":{"type":"number","description":"Numeric sort order for display purposes. Lower numbers appear first in lists and dropdowns. May be undefined if not explicitly set.","example":1},"translations":{"description":"Complete array of translations for all supported languages. Each translation contains the language code and localized name/description.","example":[{"language":"en","name":"In Progress","description":"Project is currently in progress"},{"language":"de","name":"In Bearbeitung","description":"Projekt ist derzeit in Bearbeitung"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectStatusTranslationDto"}}},"required":["id","name","icon","type","translations"]},"CreateProjectStatusDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the project status. Represents a phase in the project workflow (e.g., \"In Progress\", \"On Hold\", \"Quality Review\").","example":"In Progress"},"icon":{"type":"string","description":"Icon identifier in emoji format. Must be wrapped in colons (e.g., \":hourglass_flowing_sand:\", \":check_mark:\", \":moneybag:\"). The icon appears next to the status name throughout the application.","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Status type enum value that categorizes the status. Determines billing eligibility and workflow behavior. Valid values: Sales, Requirements, Implementation, QualityManagement, Billing, Done.","enum":["Sales","Requirements","Implementation","QualityManagement","Billing","Done"],"example":"Implementation"},"description":{"type":"object","description":"Optional description providing context about when to use this status. Helps users understand the purpose of the status.","example":"Project is currently in development phase"},"translations":{"description":"Array of translations for multilingual support. Each translation object contains a language code and translated name/description. The system automatically displays the correct language based on user preferences.","example":[{"language":"en","name":"In Progress","description":"Project is currently in progress"},{"language":"de","name":"In Bearbeitung","description":"Projekt ist derzeit in Bearbeitung"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectStatusTranslationDto"}}},"required":["name","icon","type","translations"]},"UpdateProjectStatusDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project status","example":"In Progress"},"icon":{"type":"string","description":"Icon for the project status in format :icon_name:","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Type of the project status","enum":["Sales","Requirements","Implementation","QualityManagement","Billing","Done"],"example":"Implementation"},"description":{"type":"object","description":"Description of the project status","example":"Project is currently in development phase"},"translations":{"description":"Translations for the project status","type":"array","items":{"$ref":"#/components/schemas/ProjectStatusTranslationDto"}}}},"PatchProjectStatusDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project status","example":"In Progress"},"icon":{"type":"string","description":"Icon for the project status in format :icon_name:","example":":hourglass_flowing_sand:"},"type":{"type":"string","description":"Type of the project status","enum":["Sales","Requirements","Implementation","QualityManagement","Billing","Done"],"example":"Implementation"},"description":{"type":"object","description":"Description of the project status","example":"Project is currently in development phase"},"translations":{"description":"Translations for the project status","type":"array","items":{"$ref":"#/components/schemas/ProjectStatusTranslationDto"}}}},"SortProjectStatusesDto":{"type":"object","properties":{"ids":{"description":"Array of project status IDs in the desired order","example":["status-1","status-2","status-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"ProjectPhaseTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"object","description":"Translated name of the project phase in this language. Used for multilingual display in the user interface.","example":"Planning"},"description":{"type":"object","description":"Translated description of the project phase in this language. Provides context about when to use this phase.","example":"Project planning and preparation phase"}},"required":["language"]},"ProjectPhaseResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the project phase. Used to reference this phase when creating or updating opportunities.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Display name of the project phase. Shown in dropdowns and the Sales Kanban board.","example":"Planning"},"icon":{"type":"string","description":"Icon identifier in emoji format (e.g., \":calendar:\"). Displayed next to the phase name throughout the application.","example":":calendar:"},"type":{"type":"string","description":"Phase type enum value that determines which Kanban column the opportunity appears in. Valid values: new, inProgress, done.","enum":["new","inProgress","done"],"example":"new"},"description":{"type":"object","description":"Optional description providing context about when to use this phase.","example":"Initial planning and requirement gathering phase"},"sort":{"type":"number","description":"Numeric sort order for display purposes. Lower numbers appear first in lists and dropdowns. May be undefined if not explicitly set.","example":1},"translations":{"description":"Complete array of translations for all supported languages. Each translation contains the language code and localized name/description.","example":[{"language":"en","name":"Planning","description":"Project planning phase"},{"language":"de","name":"Planung","description":"Projektplanungsphase"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectPhaseTranslationDto"}}},"required":["id","name","icon","type","translations"]},"CreateProjectPhaseDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the project phase. Represents a stage in the sales pipeline (e.g., \"Qualification\", \"Proposal Sent\", \"Negotiation\").","example":"Planning"},"icon":{"type":"string","description":"Icon identifier in emoji format. Must be wrapped in colons (e.g., \":calendar:\", \":phone:\", \":trophy:\"). The icon appears next to the phase name throughout the application.","example":":calendar:"},"type":{"type":"string","description":"Phase type enum value that determines which Kanban column the opportunity appears in. Valid values: new (appears in \"New\" column), inProgress (appears in \"In Progress\" column), done (appears in \"Done\" column).","enum":["new","inProgress","done"],"example":"new"},"description":{"type":"object","description":"Optional description providing context about when to use this phase. Helps users understand the purpose of the phase.","example":"Initial planning and requirement gathering phase"},"translations":{"description":"Array of translations for multilingual support. Each translation object contains a language code and translated name/description. The system automatically displays the correct language based on user preferences.","example":[{"language":"en","name":"Planning","description":"Project planning phase"},{"language":"de","name":"Planung","description":"Projektplanungsphase"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectPhaseTranslationDto"}}},"required":["name","icon","type","translations"]},"UpdateProjectPhaseDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project phase","example":"Planning"},"icon":{"type":"string","description":"Icon for the project phase in format :icon_name:","example":":calendar:"},"type":{"type":"string","description":"Type of the project phase","enum":["new","inProgress","done"],"example":"new"},"description":{"type":"object","description":"Description of the project phase","example":"Initial planning and requirement gathering phase"},"translations":{"description":"Translations for the project phase","type":"array","items":{"$ref":"#/components/schemas/ProjectPhaseTranslationDto"}}}},"PatchProjectPhaseDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project phase","example":"Planning"},"icon":{"type":"string","description":"Icon for the project phase in format :icon_name:","example":":calendar:"},"type":{"type":"string","description":"Type of the project phase","enum":["new","inProgress","done"],"example":"new"},"description":{"type":"object","description":"Description of the project phase","example":"Initial planning and requirement gathering phase"},"translations":{"description":"Translations for the project phase","type":"array","items":{"$ref":"#/components/schemas/ProjectPhaseTranslationDto"}}}},"SortProjectPhasesDto":{"type":"object","properties":{"ids":{"description":"Array of project phase IDs in the desired order","example":["phase-1","phase-2","phase-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"ProjectCustomFieldTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"string","description":"Translated name of the custom field in this language. Used for multilingual display in project forms.","example":"Priority Level"},"description":{"type":"string","description":"Translated description of the custom field in this language. Provides context about what information should be entered.","example":"Priority level for the project"}},"required":["language"]},"ProjectCustomFieldSelectOptionTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"string","description":"Translated display name for the select option in this language. Shown to users when selecting from dropdown options.","example":"High Priority"}},"required":["language","name"]},"ProjectCustomFieldSelectOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Optional unique identifier for the select option. If not provided, the value will be used as the identifier. Required when updating existing options.","example":"option-1"},"value":{"type":"string","description":"Display value for the select option. This is the value stored when the option is selected. Must be unique within the field.","example":"High Priority"},"translations":{"description":"Array of translations for multilingual support. Each translation contains a language code and translated display name. The system automatically shows the correct language based on user preferences.","example":[{"language":"en","name":"High Priority"},{"language":"de","name":"Hohe Priorität"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldSelectOptionTranslationDto"}}},"required":["value","translations"]},"ProjectCustomFieldResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the custom field. Used to reference this field when setting values on projects.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Display name of the custom field. Shown as the field label in project forms.","example":"Priority Level"},"description":{"type":"string","description":"Description providing context about what information should be entered in this field.","example":"Priority level for the project"},"sort":{"type":"number","description":"Numeric sort order for display purposes. Lower numbers appear first in project forms. Determines the order in which fields are displayed.","example":1},"type":{"type":"string","description":"Field type enum value indicating the input type. Valid values: Text, Textarea, Number, Date, Checkbox, Select, MultiSelect.","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"entity":{"type":"string","description":"Entity type identifier. Always \"Project\" for project custom fields. Used internally to scope fields to specific entity types.","example":"Project"},"translations":{"description":"Complete array of translations for the field name. Each translation contains the language code and localized name.","type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldTranslationDto"}},"selectOptions":{"description":"Array of select options for Select and MultiSelect field types. Each option contains an ID, value, and translations. Empty array for other field types.","type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldSelectOptionDto"}}},"required":["id","name","description","sort","type","entity","translations","selectOptions"]},"CreateProjectCustomFieldDto":{"type":"object","properties":{"id":{"type":"string","description":"Optional unique identifier for the custom field. Only needed when updating an existing field. If omitted, a new field will be created."},"name":{"type":"string","description":"Display name for the custom field. Shown as the field label in project forms (e.g., \"Priority Level\", \"Region\", \"Server Name\").","example":"Priority Level"},"description":{"type":"string","description":"Optional description providing context about what information should be entered in this field. Helps users understand the purpose of the field.","example":"Priority level for the project"},"type":{"type":"string","description":"Field type enum value that determines what kind of input is used. Valid values: Text (single-line), Textarea (multi-line), Number, Date, Checkbox, Select (single choice), MultiSelect (multiple choices).","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"selectOptions":{"description":"Array of select options. REQUIRED for Select and MultiSelect field types. Each option contains a value and translations. Not used for other field types.","type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldSelectOptionDto"}},"translations":{"description":"Array of translations for the field name. Each translation contains a language code and translated name. The system automatically displays the correct language based on user preferences.","example":[{"language":"en","name":"Priority Level"},{"language":"de","name":"Prioritätsstufe"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldTranslationDto"}},"translationsDescriptions":{"description":"Optional array of translations for the field description. Each translation contains a language code and translated description. Used for multilingual help text.","type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldTranslationDto"}}},"required":["name","type","translations"]},"UpdateProjectCustomFieldDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the custom field","example":"Priority Level"},"description":{"type":"string","description":"Description of the custom field","example":"Priority level for the project"},"type":{"type":"string","description":"Type of the custom field","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"selectOptions":{"description":"Select options (required for Select type fields)","type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldSelectOptionDto"}},"translations":{"description":"Translations for the custom field name","example":[{"language":"en","name":"Priority Level"},{"language":"de","name":"Prioritätsstufe"}],"type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldTranslationDto"}},"translationsDescriptions":{"description":"Translations for the custom field description","type":"array","items":{"$ref":"#/components/schemas/ProjectCustomFieldTranslationDto"}}},"required":["name","type","translations"]},"SortProjectCustomFieldsDto":{"type":"object","properties":{"ids":{"description":"Array of custom field IDs in the desired order","example":["field-1","field-2","field-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"RestoreProjectSettingsDto":{"type":"object","properties":{"keepCustom":{"type":"boolean","description":"Boolean flag controlling whether custom entries are preserved when restoring defaults. If false (default), all custom categories, statuses, and phases are deleted and only defaults are restored. If true, custom entries are kept and defaults are added alongside them.","example":false,"default":false}},"required":["keepCustom"]},"SupportContingentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the support contingent (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"ID of the project this contingent belongs to (UUID)","example":"550e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Title or name of the support contingent plan","example":"Maintenance flatrate"},"fromDate":{"type":"string","description":"Start date of the contingent period (ISO 8601 date format)","example":"2025-01-01"},"toDate":{"type":"string","description":"End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.","example":"2025-12-31"},"frequency":{"type":"number","description":"Frequency in months for the contingent period","example":12},"price":{"type":"number","description":"Total price for the contingent period","example":10000},"hours":{"type":"number","description":"Total hours allocated for the contingent period","example":120},"rate":{"type":"number","description":"Calculated hourly rate (price / hours). Automatically computed.","example":83.33},"transferable":{"type":"boolean","description":"Whether unused hours can be transferred to the next period","example":false},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the contingent was created","example":"2024-01-15T10:00:00Z"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp when the contingent was last updated","example":"2024-01-20T14:30:00Z"}},"required":["id","projectId","title","fromDate","frequency","price","hours","rate","transferable","createdAt","updatedAt"]},"CreateSupportContingentDto":{"type":"object","properties":{"projectId":{"type":"string","description":"ID of the project this contingent belongs to (UUID)","example":"550e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Title or name of the support contingent plan","example":"Maintenance flatrate"},"fromDate":{"type":"string","description":"Start date of the contingent period (ISO 8601 date format)","example":"2025-01-01"},"toDate":{"type":"string","description":"End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.","example":"2025-12-31"},"frequency":{"type":"number","description":"Frequency in months for the contingent period","example":12},"price":{"type":"number","description":"Total price for the contingent period","example":10000},"hours":{"type":"number","description":"Total hours allocated for the contingent period","example":120},"transferable":{"type":"boolean","description":"Whether unused hours can be transferred to the next period","example":false}},"required":["projectId","title","fromDate","frequency","price","hours"]},"UpdateSupportContingentDto":{"type":"object","properties":{"title":{"type":"string","description":"Title or name of the support contingent plan","example":"Maintenance flatrate"},"fromDate":{"type":"string","description":"Start date of the contingent period (ISO 8601 date format)","example":"2025-01-01"},"toDate":{"type":"string","description":"End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.","example":"2025-12-31"},"frequency":{"type":"number","description":"Frequency in months for the contingent period","example":12},"price":{"type":"number","description":"Total price for the contingent period","example":10000},"hours":{"type":"number","description":"Total hours allocated for the contingent period","example":120},"transferable":{"type":"boolean","description":"Whether unused hours can be transferred to the next period","example":false}},"required":["title","fromDate","frequency","price","hours"]},"PatchSupportContingentDto":{"type":"object","properties":{"title":{"type":"string","description":"Title or name of the support contingent plan","example":"Maintenance flatrate"},"fromDate":{"type":"string","description":"Start date of the contingent period (ISO 8601 date format)","example":"2025-01-01"},"toDate":{"type":"string","description":"End date of the contingent period (ISO 8601 date format)","example":"2025-12-31"},"frequency":{"type":"number","description":"Frequency in months for the contingent period","example":12},"price":{"type":"number","description":"Total price for the contingent period","example":10000},"hours":{"type":"number","description":"Total hours allocated for the contingent period","example":120},"transferable":{"type":"boolean","description":"Whether unused hours can be transferred to the next period","example":false}}},"ImportsGridResponse":{"type":"object","properties":{"items":{"type":"array","description":"Array of import items in the current page. Each item represents a CSV import with its metadata and status.","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the import"},"fileName":{"type":"string","nullable":true,"description":"Original name of the uploaded CSV file"},"status":{"type":"string","description":"Current status: \"Uploaded\" (ready), \"Processing\" (running), \"Completed\" (success), or \"Failed\" (error)"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when the import was created"},"importedAt":{"type":"string","format":"date-time","nullable":true,"description":"ISO 8601 timestamp when the import finished processing (null if not completed)"},"rowCount":{"type":"number","description":"Total number of data rows in the CSV file"},"columnCount":{"type":"number","description":"Total number of columns in the CSV file"}}}},"total":{"type":"number","description":"Total number of imports matching the current filters (across all pages)","example":42},"page":{"type":"number","description":"Current page number (1-based indexing)","example":1},"pageSize":{"type":"number","description":"Number of items per page in this response","example":100},"viewId":{"type":"string","description":"View identifier for saved grid configurations (e.g., \"all\", \"recent\", custom view name)","example":"all"}},"required":["items","total","page","pageSize","viewId"]},"UploadImportResponseDto":{"type":"object","properties":{"importId":{"type":"string","description":"Unique identifier (UUID) for the newly created import. Use this ID to fetch details, update mappings, or execute the import.","example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364"},"fileName":{"type":"string","description":"Original file name of the uploaded CSV file","example":"employees.csv"},"rowCount":{"type":"number","description":"Total number of data rows in the CSV file (excluding header row)","example":150},"columnCount":{"type":"number","description":"Total number of columns detected in the CSV file","example":12},"status":{"type":"string","description":"Current status of the import. Will be \"Uploaded\" immediately after upload, indicating the import is ready for mapping configuration.","example":"Uploaded","enum":["Uploaded","Processing","Completed","Failed"]},"mappings":{"type":"array","description":"AI-generated mapping suggestions in AI format. Each mapping specifies an entity type (e.g., \"Employee\", \"Project\") and suggested field mappings from CSV columns to entity fields. Review and modify these before executing the import.","items":{"type":"object","properties":{"type":{"type":"string","description":"Entity type to import: \"Employee\", \"Organization\", \"OrganizationMember\", \"Project\", \"Task\", or \"TaskComment\""},"fields":{"type":"object","description":"Field mappings object. Keys are entity field names, values contain mapTo (column UUID), optional defaultValue, and optional valueMappings for enums"},"customLookupFields":{"type":"array","items":{"type":"string"},"description":"Optional array of field names (including custom fields like \"customField_abc123\") to use for finding existing records. If not provided, uses entity default lookup fields."}}}},"columns":{"type":"array","description":"Metadata for each column in the CSV file, including column UUID (use this in mappings), name, detected data type, and sample values. Column UUIDs are required when creating field mappings."},"sampleRows":{"type":"array","description":"First 10 rows of data from the CSV file for preview. Useful for understanding the data structure and validating mappings."},"aiMappingSuccess":{"type":"boolean","description":"Indicates whether AI mapping generation completed successfully. If false, mappings array may be empty or incomplete, and you may need to manually configure mappings.","example":true}},"required":["importId","fileName","rowCount","columnCount","status","mappings","columns","sampleRows","aiMappingSuccess"]},"GetImportDetailsResponseDto":{"type":"object","properties":{"importId":{"type":"string","description":"Unique identifier (UUID) of the import","example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364"},"fileName":{"type":"object","description":"Original name of the uploaded CSV file","example":"employees.csv","nullable":true},"rowCount":{"type":"number","description":"Total number of data rows in the CSV file (excluding header)","example":150},"columnCount":{"type":"number","description":"Total number of columns detected in the CSV file","example":12},"status":{"type":"string","description":"Current status of the import: \"Uploaded\" (ready), \"Processing\" (running), \"Completed\" (success), or \"Failed\" (error). Check processedEntities vs totalToProcess to see progress during processing.","example":"Uploaded","enum":["Uploaded","Processing","Completed","Failed"]},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp when the import was created","example":"2024-01-15T10:30:00Z"},"importedAt":{"type":"object","description":"ISO 8601 timestamp when the import finished processing. Null if import has not completed yet.","example":"2024-01-15T10:35:00Z","nullable":true},"mappings":{"type":"array","description":"All current field mappings in AI format. Each mapping specifies entity type, field mappings (CSV column UUIDs to entity fields), value mappings, and custom lookup fields. Use these mappings to understand how CSV data will be imported."},"columns":{"type":"array","description":"Metadata for each CSV column including UUID (required for mappings), name, detected data type, and sample values. Column UUIDs are used in field mappings."},"sampleRows":{"type":"array","description":"First 10 rows of data from the CSV file for preview. Useful for understanding data structure and validating that mappings are correct."},"fileSize":{"type":"object","description":"Size of the uploaded CSV file in bytes","example":52480,"nullable":true},"mimeType":{"type":"object","description":"MIME type of the uploaded file (typically \"text/csv\", \"application/csv\", or \"text/plain\")","example":"text/csv","nullable":true},"processedEntities":{"type":"number","description":"Number of entities that have been processed so far. Compare with totalToProcess to calculate progress percentage. Only meaningful when status is \"Processing\" or \"Completed\".","example":100},"totalToProcess":{"type":"number","description":"Total number of entities that need to be processed. This is calculated based on the number of rows and mappings. Compare with processedEntities to track progress.","example":150},"totalCreatedEntities":{"type":"number","description":"Total number of new entities that were created during import execution. Only available after import completes.","example":80},"totalUpdatedEntities":{"type":"number","description":"Total number of existing entities that were updated during import execution. Only available after import completes.","example":15},"totalSkippedEntities":{"type":"number","description":"Total number of rows that were skipped during import execution (due to validation errors, missing required fields, etc.). Only available after import completes.","example":5}},"required":["importId","fileName","rowCount","columnCount","status","createdAt","importedAt","mappings","columns","sampleRows","fileSize","mimeType","processedEntities","totalToProcess","totalCreatedEntities","totalUpdatedEntities","totalSkippedEntities"]},"UpdateImportMappingsRequestDto":{"type":"object","properties":{"mappings":{"type":"array","description":"Array of mapping configurations in AI format. Each mapping specifies an entity type and how CSV columns map to entity fields. This replaces all existing mappings for the import.","items":{"type":"object","properties":{"type":{"type":"string","description":"Entity type to import: \"Employee\", \"Organization\", \"OrganizationMember\", \"Project\", \"Task\", or \"TaskComment\"","example":"Employee"},"fields":{"type":"object","description":"Field mappings object. Keys are entity field names (or \"customField_{fieldId}\" for custom fields). Values are objects with: mapTo (required, CSV column UUID), defaultValue (optional), and valueMappings (optional, for enum conversions).","example":{"firstName":{"mapTo":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364"},"lastName":{"mapTo":"a3ff87f4-06e8-5c09-9e6e-09ed2gc80475"},"email":{"mapTo":"b4gg98g5-17f9-6d10-0f7f-10fe3hd91586"},"role":{"mapTo":"c5hh09h6-28g0-7e11-1g8g-21gf4ie02697","valueMappings":{"Manager":"manager","Developer":"developer"}},"customField_abc123":{"mapTo":"d6ii10i7-39h1-8f12-2h9h-32hg5jf13708"}}},"customLookupFields":{"type":"array","items":{"type":"string"},"description":"Optional array of field names to use for finding existing records. Can include built-in fields (e.g., \"email\", \"firstName\") or custom fields (e.g., \"customField_abc123\"). If a record matches on these fields, it will be updated; otherwise, a new record will be created. If not provided, uses the entity's default requiredToUpdate fields.","example":["email"]}}},"example":[{"type":"Employee","fields":{"firstName":{"mapTo":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364"},"lastName":{"mapTo":"a3ff87f4-06e8-5c09-9e6e-09ed2gc80475"},"email":{"mapTo":"b4gg98g5-17f9-6d10-0f7f-10fe3hd91586"}},"customLookupFields":["email"]}]}},"required":["mappings"]},"UpdateImportMappingsResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Indicates whether the mapping update was successful. If true, all mappings have been saved and validated.","example":true},"mappings":{"type":"array","description":"Updated mappings in AI format, including any automatically added dependency mappings (e.g., Organization mapping if OrganizationMember was specified). Use these mappings to verify the final configuration."}},"required":["success","mappings"]},"ImportSummaryDto":{"type":"object","properties":{"totalRows":{"type":"number","description":"Total number of rows that were processed during the dry-run or execution","example":150},"willCreate":{"type":"number","description":"Number of new entities that will be created (for dry-run) or were created (for execution). These are rows where no matching existing record was found using the lookup fields.","example":100},"willUpdate":{"type":"number","description":"Number of existing entities that will be updated (for dry-run) or were updated (for execution). These are rows where a matching existing record was found using the lookup fields.","example":40},"willSkip":{"type":"number","description":"Number of rows that will be skipped (for dry-run) or were skipped (for execution). These rows had validation errors, missing required fields, or other issues that prevented processing.","example":10},"errors":{"type":"number","description":"Number of rows that have validation errors. These are included in willSkip but counted separately to highlight data quality issues. Check the errors array for detailed error messages.","example":5}},"required":["totalRows","willCreate","willUpdate","willSkip","errors"]},"DryRunResponseDto":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"Always true for dry-run operations. Indicates this is a preview simulation, not an actual execution.","example":true},"mappings":{"type":"array","description":"Detailed results for each entity type mapping. Each result includes lists of entities that will be created (toCreate), updated (toUpdate with old/new values), and skipped (ignored with reasons). Use this to understand exactly what will happen for each entity type."},"summary":{"description":"Summary statistics aggregating results across all mappings. Provides quick overview of total rows, create/update/skip counts, and error count.","allOf":[{"$ref":"#/components/schemas/ImportSummaryDto"}]},"errors":{"type":"array","description":"List of all rows that will be skipped due to errors, including row index, the data from that row, and the specific error message explaining why it will be skipped. Use this to identify and fix data quality issues before execution."}},"required":["dryRun","mappings","summary","errors"]},"ExecuteImportResponseDto":{"type":"object","properties":{"queued":{"type":"boolean","description":"Indicates whether the import has been successfully queued for background processing. If true, the import will start processing asynchronously. Use GET /{importId} to monitor progress.","example":true},"importId":{"type":"string","description":"The import ID (UUID) that was queued. Use this ID with GET /{importId} to check status and monitor progress during background processing.","example":"d2ee76e3-95d7-4b98-8d5d-98dc1fb79364"},"message":{"type":"string","description":"Human-readable message explaining the import status and next steps for tracking progress","example":"Import has been queued for processing. Use GET /{importId} to check status."}},"required":["queued","importId","message"]},"ProjectChartSheetDataDto":{"type":"object","properties":{"headers":{"description":"Column headers for the sheet","example":["Period","Period Label","Value","Cumulative Total"],"type":"array","items":{"type":"string"}},"rows":{"type":"array","description":"Data rows as array of objects, where keys match headers. Each row represents one data point.","items":{"type":"object"},"example":[{"Period":"2024-01","Period Label":"Jan 2024","Value":120.5,"Cumulative Total":120.5},{"Period":"2024-02","Period Label":"Feb 2024","Value":135.2,"Cumulative Total":255.7}]},"title":{"type":"object","description":"Sheet title information with main title and subtitle","example":{"main":"Project Insights: Project Name","subtitle":"Jan 2024 - Dec 2024, Time Series, By User, Months, Trend"}}},"required":["headers","rows","title"]},"ProjectChartDataStructuredResponseDto":{"type":"object","properties":{"summary":{"description":"Summary sheet data (wide/matrix format, like pivot table). Periods as rows, breakdown categories as columns.","allOf":[{"$ref":"#/components/schemas/ProjectChartSheetDataDto"}]},"detailed":{"description":"Detailed sheet data (long/tidy format, normalized). One row per period/breakdown combination.","allOf":[{"$ref":"#/components/schemas/ProjectChartSheetDataDto"}]},"metadata":{"type":"object","description":"Metadata about the query execution and results","example":{"queryExecutionTime":234,"totalTimeLogsProcessed":1000,"periodsGenerated":12,"hasMoreData":false}}},"required":["summary","detailed","metadata"]},"StaffChartSheetDataDto":{"type":"object","properties":{"headers":{"description":"Column headers for the sheet","example":["Period","Period Label","Employee","Hours"],"type":"array","items":{"type":"string"}},"rows":{"type":"array","description":"Data rows as array of objects, where keys match headers. Each row represents one data point.","items":{"type":"object"},"example":[{"Period":"2024-01","Period Label":"Jan 2024","Employee":"John Doe","Hours":160},{"Period":"2024-02","Period Label":"Feb 2024","Employee":"John Doe","Hours":175}]},"title":{"type":"object","description":"Sheet title information with main title and subtitle","example":{"main":"Staff Insights: John Doe","subtitle":"Comparison, Breakdown by Project, Jan 2024 - Dec 2024, By Months"}}},"required":["headers","rows","title"]},"StaffChartDataStructuredResponseDto":{"type":"object","properties":{"summary":{"description":"Summary sheet data. For COMPARISON model: wide format with periods/staff as rows and breakdown categories as columns. For TOTAL model: categories with hours and percentage.","allOf":[{"$ref":"#/components/schemas/StaffChartSheetDataDto"}]},"detailed":{"description":"Detailed sheet data. For COMPARISON model: long format (normalized) with one row per period/staff/breakdown combination. For TOTAL model: same as summary.","allOf":[{"$ref":"#/components/schemas/StaffChartSheetDataDto"}]},"metadata":{"type":"object","description":"Metadata about the query execution and results","example":{"queryExecutionTime":234,"totalTimeLogsProcessed":480,"periodsGenerated":12,"hasMoreData":false}}},"required":["summary","detailed","metadata"]},"TurnoverChartDataStructuredResponseDto":{"type":"object","properties":{"summary":{"description":"Summary sheet data (wide/matrix format, like pivot table). Layout depends on `model` and `breakdown` (e.g. comparison+byProject vs comparison+byRevenueType vs total).","allOf":[{"$ref":"#/components/schemas/ProjectChartSheetDataDto"}]},"detailed":{"description":"Detailed sheet data (long/tidy format, normalized). One row per period with project and/or revenue-type columns depending on model and breakdown.","allOf":[{"$ref":"#/components/schemas/ProjectChartSheetDataDto"}]},"forecast":{"description":"Forecast sheet data (only present when includeTrend=true and forecast data exists). Contains 6-month revenue forecast based on subscription billing and project deadlines.","allOf":[{"$ref":"#/components/schemas/ProjectChartSheetDataDto"}]},"metadata":{"type":"object","description":"Metadata about the query execution and results","example":{"queryExecutionTime":234,"totalInvoicesProcessed":120,"periodsGenerated":12,"hasMoreData":false}}},"required":["summary","detailed","metadata"]},"GoalChartSheetDataDto":{"type":"object","properties":{"headers":{"description":"Column headers for the sheet","example":["Employee","Attendance Goal","Attendance Actual","Booked Goal","Booked Actual","Billable Goal","Billable Actual"],"type":"array","items":{"type":"string"}},"rows":{"type":"array","description":"Data rows as array of objects, where keys match headers. Each row represents one employee or aggregated total.","items":{"type":"object"},"example":[{"Employee":"John Doe","Attendance Goal":160,"Attendance Actual":152,"Booked Goal":160,"Booked Actual":148,"Billable Goal":120,"Billable Actual":115},{"Employee":"Jane Smith","Attendance Goal":160,"Attendance Actual":160,"Booked Goal":160,"Booked Actual":160,"Billable Goal":120,"Billable Actual":125}]},"title":{"type":"object","description":"Sheet title information with main title and subtitle","example":{"main":"Goal Insights - Multiple Staff","subtitle":"This Month | Individual"}}},"required":["headers","rows","title"]},"GoalChartDataStructuredResponseDto":{"type":"object","properties":{"goalProgress":{"description":"Goal progress sheet data: Employee time tracking goals (attendance, booked, billable) with goals and actuals","allOf":[{"$ref":"#/components/schemas/GoalChartSheetDataDto"}]},"valueDistribution":{"description":"Value distribution sheet data: Breakdown of hours by value groups (A, B, C, D)","allOf":[{"$ref":"#/components/schemas/GoalChartSheetDataDto"}]},"metadata":{"type":"object","description":"Metadata about the query execution and results","example":{"queryExecutionTime":234,"totalEmployeesProcessed":2,"attendanceEnabled":true}}},"required":["goalProgress","valueDistribution","metadata"]},"WorkloadChartSheetDataDto":{"type":"object","properties":{"headers":{"description":"Column headers for the sheet","example":["Period Label","Tasks"],"type":"array","items":{"type":"string"}},"rows":{"type":"array","description":"Data rows as array of objects, where keys match headers. Each row represents one data point.","items":{"type":"object"},"example":[{"Period Label":"Jan 2024","Tasks":45},{"Period Label":"Feb 2024","Tasks":52}]},"title":{"type":"object","description":"Sheet title information with main title and subtitle","example":{"main":"Workload Insights: Project Name","subtitle":"Over Time, Breakdown by Task Type, Jan 2024 - Dec 2024, By Months"}}},"required":["headers","rows","title"]},"WorkloadChartDataStructuredResponseDto":{"type":"object","properties":{"summary":{"description":"Summary sheet data (simple format). Period labels as rows, task counts as columns.","allOf":[{"$ref":"#/components/schemas/WorkloadChartSheetDataDto"}]},"detailed":{"description":"Detailed sheet data (long/normalized format). One row per period/breakdown combination.","allOf":[{"$ref":"#/components/schemas/WorkloadChartSheetDataDto"}]},"metadata":{"type":"object","description":"Metadata about the query execution and results","example":{"queryExecutionTime":234,"totalTasksProcessed":1000,"periodsGenerated":12,"hasMoreData":false}}},"required":["summary","detailed","metadata"]},"EmailAccountStatus":{"type":"string","enum":["active","token_expired","smtp_error","imap_error","disconnected"],"description":"Current operational status of the account. Indicates whether the account is working correctly or has encountered issues."},"EmailAccountCapabilitiesDto":{"type":"object","properties":{"canEdit":{"type":"boolean","description":"Whether the account can be edited via API. This is `true` for IMAP accounts and `false` for OAuth accounts (Google, Office365) which must be managed through the web application for security reasons.","example":true},"canDelete":{"type":"boolean","description":"Whether the account can be deleted via API. This is `true` for all account types, including OAuth accounts.","example":true},"reason":{"type":"string","description":"Optional reason explaining why certain operations are restricted. This field is only present for OAuth accounts to explain why they cannot be created or updated via API.","example":"OAuth accounts can only be managed through the web application"}},"required":["canEdit","canDelete"]},"EmailAccountResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the email account","example":"123e4567-e89b-12d3-a456-426614174000","format":"uuid"},"email":{"type":"string","description":"Email address associated with this account. Used for sending and receiving emails.","example":"support@example.com"},"displayName":{"type":"object","description":"Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.","example":"Support Team"},"type":{"type":"string","description":"Type of email account. Determines how the account is authenticated and what operations are available via API.","enum":["imap","google","office365"],"example":"imap"},"status":{"description":"Current operational status of the account. Indicates whether the account is working correctly or has encountered issues.","example":"active","allOf":[{"$ref":"#/components/schemas/EmailAccountStatus"}]},"lastError":{"type":"object","description":"Most recent error message, if the account has encountered any issues. This helps diagnose connection or authentication problems.","example":"Failed to connect to SMTP server"},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when the account was created","example":"2024-01-15T10:30:00Z","format":"date-time"},"editedAt":{"type":"string","description":"ISO 8601 timestamp indicating when the account was last modified","example":"2024-01-20T14:45:00Z","format":"date-time"},"capabilities":{"description":"Account capabilities indicating what operations can be performed via API. Includes restrictions for OAuth accounts.","allOf":[{"$ref":"#/components/schemas/EmailAccountCapabilitiesDto"}]}},"required":["id","email","type","status","createdAt","editedAt","capabilities"]},"CreateEmailAccountDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address for the account. Must be unique within the workspace and will be used for sending and receiving emails.","example":"support@example.com"},"displayName":{"type":"string","description":"Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.","example":"Support Team"},"imapsHost":{"type":"string","description":"IMAP server hostname for receiving emails. This is the server address where the email client connects to retrieve incoming emails (e.g., \"imap.gmail.com\" for Gmail, \"outlook.office365.com\" for Office365).","example":"imap.example.com"},"imapsPort":{"type":"number","description":"IMAP server port number. Typically 993 for SSL/TLS connections or 143 for unencrypted connections. Must be between 1-65535.","example":993,"minimum":1,"maximum":65535},"imapsUser":{"type":"string","description":"Username for authenticating with the IMAP server. This is typically the full email address, but may vary depending on your email provider.","example":"support@example.com"},"imapsPassword":{"type":"string","description":"Password for authenticating with the IMAP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.","example":"secure-password"},"smtpHost":{"type":"string","description":"SMTP server hostname for sending emails. This is the server address where the email client connects to send outgoing emails (e.g., \"smtp.gmail.com\" for Gmail, \"smtp.office365.com\" for Office365).","example":"smtp.example.com"},"smtpPort":{"type":"number","description":"SMTP server port number. Typically 587 for STARTTLS, 465 for SSL/TLS, or 25 for unencrypted connections. Must be between 1-65535.","example":587,"minimum":1,"maximum":65535},"smtpUser":{"type":"string","description":"Username for authenticating with the SMTP server. This is typically the full email address, but may vary depending on your email provider.","example":"support@example.com"},"smtpPassword":{"type":"string","description":"Password for authenticating with the SMTP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.","example":"secure-password"}},"required":["email","imapsHost","imapsPort","imapsUser","imapsPassword","smtpHost","smtpPort","smtpUser","smtpPassword"]},"UpdateEmailAccountDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address for the account. Must be unique within the workspace and will be used for sending and receiving emails.","example":"support@example.com"},"displayName":{"type":"string","description":"Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.","example":"Support Team"},"imapsHost":{"type":"string","description":"IMAP server hostname for receiving emails. This is the server address where the email client connects to retrieve incoming emails (e.g., \"imap.gmail.com\" for Gmail, \"outlook.office365.com\" for Office365).","example":"imap.example.com"},"imapsPort":{"type":"number","description":"IMAP server port number. Typically 993 for SSL/TLS connections or 143 for unencrypted connections. Must be between 1-65535.","example":993,"minimum":1,"maximum":65535},"imapsUser":{"type":"string","description":"Username for authenticating with the IMAP server. This is typically the full email address, but may vary depending on your email provider.","example":"support@example.com"},"imapsPassword":{"type":"string","description":"Password for authenticating with the IMAP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.","example":"secure-password"},"smtpHost":{"type":"string","description":"SMTP server hostname for sending emails. This is the server address where the email client connects to send outgoing emails (e.g., \"smtp.gmail.com\" for Gmail, \"smtp.office365.com\" for Office365).","example":"smtp.example.com"},"smtpPort":{"type":"number","description":"SMTP server port number. Typically 587 for STARTTLS, 465 for SSL/TLS, or 25 for unencrypted connections. Must be between 1-65535.","example":587,"minimum":1,"maximum":65535},"smtpUser":{"type":"string","description":"Username for authenticating with the SMTP server. This is typically the full email address, but may vary depending on your email provider.","example":"support@example.com"},"smtpPassword":{"type":"string","description":"Password for authenticating with the SMTP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.","example":"secure-password"}},"required":["email","imapsHost","imapsPort","imapsUser","imapsPassword","smtpHost","smtpPort","smtpUser","smtpPassword"]},"PatchEmailAccountDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address for the account","example":"support@example.com"},"displayName":{"type":"string","description":"Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.","example":"Support Team"},"imapsHost":{"type":"string","description":"IMAP server hostname","example":"imap.example.com"},"imapsPort":{"type":"number","description":"IMAP server port (typically 993 for SSL)","example":993},"imapsUser":{"type":"string","description":"IMAP username","example":"support@example.com"},"imapsPassword":{"type":"string","description":"IMAP password","example":"secure-password"},"smtpHost":{"type":"string","description":"SMTP server hostname","example":"smtp.example.com"},"smtpPort":{"type":"number","description":"SMTP server port (typically 587 for TLS or 465 for SSL)","example":587},"smtpUser":{"type":"string","description":"SMTP username","example":"support@example.com"},"smtpPassword":{"type":"string","description":"SMTP password","example":"secure-password"}}},"TestEmailSendingDto":{"type":"object","properties":{"testEmailAddress":{"type":"string","description":"Email address where the test email should be sent. This should be a valid email address that you can access to verify the test email was received.","example":"test@example.com"}},"required":["testEmailAddress"]},"TestEmailSendingResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Indicates whether the test email was sent successfully. `true` means the email was sent, `false` means there was an error.","example":true},"message":{"type":"string","description":"Human-readable message describing the test result. Includes success confirmation or error details explaining what went wrong.","example":"Test email sent successfully"}},"required":["success","message"]},"TestEmailReceivingResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Indicates whether the test was successful. `true` means the connection was established and an email was retrieved, `false` means there was an error.","example":true},"message":{"type":"string","description":"Human-readable message describing the test result. Includes success confirmation or error details explaining what went wrong.","example":"Successfully connected and retrieved latest email"},"messageId":{"type":"string","description":"Message ID of the most recent email retrieved from the inbox. This is a unique identifier assigned by the email server.","example":"<abc123@example.com>"},"subject":{"type":"string","description":"Subject line of the most recent email retrieved from the inbox.","example":"Test Email Subject"},"from":{"type":"object","description":"Sender email address(es) of the most recent email. Multiple addresses are comma-separated if present.","example":"sender@example.com"},"date":{"type":"string","description":"ISO 8601 timestamp indicating when the most recent email was received.","example":"2024-01-20T10:30:00Z","format":"date-time"}},"required":["success","message"]},"VatRateResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the VAT rate. Use this ID to update or delete the rate. This ID is generated automatically when the rate is created.","example":"123e4567-e89b-12d3-a456-426614174000"},"vat":{"type":"number","description":"The VAT (Value Added Tax) rate percentage currently configured. This is a number between 0 and 100, where 21 represents 21% tax. This is the percentage that will be applied to invoices and quotes created on or after the effective date.","example":21},"dateFrom":{"type":"string","description":"The effective start date for this VAT rate in ISO 8601 format. This date determines when the rate becomes active. The system uses this date to select the correct VAT rate when creating invoices and quotes. The rate with the most recent dateFrom that is less than or equal to the document creation date is used.","example":"2024-01-01T00:00:00.000Z","format":"date-time"},"isDefault":{"type":"boolean","description":"Indicates whether this VAT rate is marked as the default rate for the workspace. Default rates cannot be deleted and typically serve as a fallback option. When true, this rate is protected from deletion operations.","example":false}},"required":["id","vat","dateFrom","isDefault"]},"CreateVatRateDto":{"type":"object","properties":{"vat":{"type":"number","description":"The VAT (Value Added Tax) rate percentage that will be applied to invoices and quotes. This is a number between 0 and 100, where 21 represents 21% tax. The system uses this rate for all documents created on or after the effective date (dateFrom).","example":21,"minimum":0,"maximum":100},"dateFrom":{"type":"string","description":"The effective start date for this VAT rate. This date determines when the rate becomes active. The system automatically applies this rate to all invoices and quotes created on or after this date. You can set a future date to plan ahead for tax law changes. The date must be provided in ISO 8601 format (YYYY-MM-DD). The system validates that this date does not overlap with existing VAT rate periods.","example":"2024-01-01","format":"date"},"isDefault":{"type":"boolean","description":"Whether this VAT rate is the default rate for the workspace. Default rates cannot be deleted and serve as a fallback. If not specified, defaults to false. Only one rate should typically be marked as default, though the system does not enforce this.","example":false,"default":false}},"required":["vat","dateFrom"]},"UpdateVatRateDto":{"type":"object","properties":{"vat":{"type":"number","description":"The VAT (Value Added Tax) rate percentage that will be applied to invoices and quotes. This is a number between 0 and 100, where 21 represents 21% tax. Updating this value changes the tax percentage for all future documents that use this rate.","example":21,"minimum":0,"maximum":100},"dateFrom":{"type":"string","description":"The effective start date for this VAT rate. This date determines when the rate becomes active. Updating this date changes which invoices and quotes will use this rate. The date must be provided in ISO 8601 format (YYYY-MM-DD). The system validates that the updated date does not overlap with other VAT rate periods.","example":"2024-01-01","format":"date"},"isDefault":{"type":"boolean","description":"Whether this VAT rate is the default rate for the workspace. Default rates cannot be deleted and serve as a fallback. If not specified, defaults to false. Only one rate should typically be marked as default, though the system does not enforce this.","example":false,"default":false}},"required":["vat","dateFrom"]},"HelpCenterSearchResultDto":{"type":"object","properties":{"id":{"type":"string","example":"65f8187b4cc7fe3ddf0f1a3a","description":"Gleap article identifier."},"title":{"type":"string","example":"What are task types?","description":"Localized article title returned by Gleap search."},"description":{"type":"string","example":"Learn how task types structure your workflows and required fields.","description":"Short article summary returned by Gleap search."},"helpcenterCollection":{"type":"string","example":"65f818364cc7fe3ddf0f195a","description":"Gleap help center collection identifier."},"docId":{"type":"number","example":1234,"description":"Human-friendly Gleap document number."},"relevanceScore":{"type":"number","example":12.345678,"description":"Search relevance score from Gleap."}},"required":["id","title","description","helpcenterCollection","docId","relevanceScore"]},"HelpCenterSearchResponseDto":{"type":"object","properties":{"searchTerm":{"type":"string","example":"task types","description":"Echo of the submitted search term."},"totalResults":{"type":"number","example":2,"description":"Number of returned search results."},"results":{"description":"Matching help center articles.","type":"array","items":{"$ref":"#/components/schemas/HelpCenterSearchResultDto"}}},"required":["searchTerm","totalResults","results"]},"HelpCenterArticleResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"65f8187b4cc7fe3ddf0f1a3a","description":"Gleap article identifier."},"title":{"type":"string","example":"What are task types?","description":"Localized article title."},"description":{"type":"string","example":"Learn how task types structure your workflows and required fields.","description":"Localized article summary."},"content":{"type":"string","example":"Task types define the workflow, fields, and statuses used when creating tasks.","description":"Plain-text article body for the selected language."},"docId":{"type":"number","example":1234,"description":"Human-friendly Gleap document number."},"isDraft":{"type":"boolean","example":false,"description":"Whether the article is currently a draft in Gleap."},"tags":{"example":["tasks","workflow"],"description":"Article tags returned by Gleap.","type":"array","items":{"type":"string"}},"lastUpdated":{"type":"string","example":"2026-03-01T11:22:33.000Z","description":"Last update timestamp from Gleap."}},"required":["id","title","description","content","docId","isDraft","tags","lastUpdated"]},"QuestionOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Option ID (for updates)","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"title":{"type":"string","description":"Option title. For Checkbox questions, users can select multiple options. For Radio questions, users can select only one option.","example":"Option A"},"extraHours":{"type":"number","description":"Extra hours for this option. When this option is selected (for Checkbox/Radio questions), this value is added to the effort calculation.","example":2.5}},"required":["title"]},"QuestionConditionDto":{"type":"object","properties":{"id":{"type":"string","description":"Condition ID (for updates)","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"type":{"type":"string","description":"Condition type. Conditions control when items or questions are visible/active:\n- **QuestionAnswered/QuestionNotAnswered**: Based on whether a question is answered\n- **QuestionAnswerEqual/QuestionAnswerDoesNotEqual**: Based on specific answer value\n- **QuestionAnswerContains/QuestionAnswerDoesNotContain**: Based on answer containing values\n- **TodoIsDone/TodoIsNotDone**: Based on todo completion status\n- **TestCasePassed/TestCaseFailed**: Based on test case evaluation","enum":["QuestionAnswered","QuestionNotAnswered","QuestionAnswerContains","QuestionAnswerDoesNotContain","QuestionAnswerEqual","QuestionAnswerDoesNotEqual","TodoIsDone","TodoIsNotDone","TestCasePassed","TestCaseFailed"],"example":"QuestionAnswered"},"targetQuestionId":{"type":"string","description":"Target question ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetTodoId":{"type":"string","description":"Target todo ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetTestCaseId":{"type":"string","description":"Target test case ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetValue":{"type":"string","description":"Target value","example":"Yes"},"containsType":{"type":"string","description":"Contains type","enum":["Some","All"],"example":"Some"}},"required":["type"]},"CreateQuestionDto":{"type":"object","properties":{"id":{"type":"string","description":"Question ID (for updates - if provided, updates existing question)","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"title":{"type":"string","description":"Question title/heading. Questions capture customer-specific requirements or project details. They make work packages flexible and reusable by allowing later individualization in projects.","example":"What is the expected performance?"},"question":{"type":"string","description":"The question text that will be displayed to users. This is the actual question being asked (e.g., \"Please specify the expected response time\").","example":"Please specify the expected response time"},"description":{"type":"string","description":"Question description (accepts HTML or Markdown, returns HTML)","example":"<p>This question helps us understand performance requirements.</p>"},"type":{"type":"string","description":"Question type. Questions can be:\n- **ShortText**: Single-line text input\n- **Editor**: Formatted multi-line text\n- **Checkbox**: Yes/No selection (can impact effort)\n- **Radio**: Single choice from options (can impact effort)\n- **Files**: Upload files\n- **Datepicker**: Select a date\n- **Multiplier**: Dynamic calculation (quantity × duration per element)\n- **Person**: Select person(s) from team","enum":["ShortText","Editor","Checkbox","Radio","Files","Datepicker","Multiplier","Person"],"example":"ShortText"},"options":{"description":"Question options (required for Checkbox/Radio types, must have at least one option). Each option can have an extraHours value that affects effort calculation when selected.","type":"array","items":{"$ref":"#/components/schemas/QuestionOptionDto"}},"multiplierValue":{"type":"number","description":"Multiplier value (for Multiplier type). This is the duration per element (e.g., hours per page). When answered, the system calculates: Answer × multiplierValue = Total additional effort.","example":1.5},"conditions":{"description":"Question conditions that control visibility/behavior. Conditions can show/hide questions based on answers to other questions, todo completion, or test case status.","type":"array","items":{"$ref":"#/components/schemas/QuestionConditionDto"}}},"required":["title","question","type"]},"UpdateQuestionDto":{"type":"object","properties":{"title":{"type":"string","description":"Question title/heading. Questions capture customer-specific requirements or project details.","example":"What is the expected performance?"},"question":{"type":"string","description":"The question text that will be displayed to users.","example":"Please specify the expected response time"},"description":{"type":"string","description":"Question description (accepts HTML or Markdown, returns HTML)","example":"<p>This question helps us understand performance requirements.</p>"},"type":{"type":"string","description":"Question type. Questions can be:\n- **ShortText**: Single-line text input\n- **Editor**: Formatted multi-line text\n- **Checkbox**: Yes/No selection (can impact effort)\n- **Radio**: Single choice from options (can impact effort)\n- **Files**: Upload files\n- **Datepicker**: Select a date\n- **Multiplier**: Dynamic calculation (quantity × duration per element)\n- **Person**: Select person(s) from team","enum":["ShortText","Editor","Checkbox","Radio","Files","Datepicker","Multiplier","Person"]},"options":{"description":"Question options (required for Checkbox/Radio types)","type":"array","items":{"$ref":"#/components/schemas/QuestionOptionDto"}},"multiplierValue":{"type":"number","description":"Multiplier value (for Multiplier type). This is the duration per element (e.g., hours per page). When answered, the system calculates: Answer × multiplierValue = Total additional effort.","example":1.5},"conditions":{"description":"Question conditions that control visibility/behavior. Conditions can show/hide questions based on answers to other questions, todo completion, or test case status.","type":"array","items":{"$ref":"#/components/schemas/QuestionConditionDto"}}}},"SortQuestionsDto":{"type":"object","properties":{"newSortOrder":{"description":"Array of question IDs in new order. All questions for the item must be included in the desired display order.","example":["e57dc37b-7693-4d06-b49c-17084b773aff","f68ed48c-8704-5e17-c5ad-28195c884b00"],"type":"array","items":{"type":"string"}}},"required":["newSortOrder"]},"CreateComponentDto":{"type":"object","properties":{"name":{"type":"string","description":"Component name","example":"User Authentication Module"},"icon":{"type":"object","description":"Icon identifier. Format: :icon_name: (e.g., :rocket:, :box:, :star:)","example":":box:","nullable":true},"description":{"type":"string","description":"Description (HTML or Markdown)","example":"<p>This module handles user authentication and authorization.</p>"},"tags":{"description":"Tag UUIDs","example":["550e8400-e29b-41d4-a716-446655440000"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note (HTML or Markdown, optional)","example":"<p>Internal implementation notes.</p>"}},"required":["name","description"]},"SaveComponentItemConditionDto":{"type":"object","properties":{"id":{"type":"string","description":"Condition ID (optional, for updates)","example":"550e8400-e29b-41d4-a716-446655440013","nullable":true},"type":{"type":"string","description":"Condition type","enum":["QuestionAnswered","QuestionNotAnswered","QuestionAnswerContains","QuestionAnswerDoesNotContain","QuestionAnswerEqual","QuestionAnswerDoesNotEqual","TodoIsDone","TodoIsNotDone","TestCasePassed","TestCaseFailed"],"example":"QuestionAnswered"},"targetQuestionId":{"type":"string","description":"Target question ID (optional)","example":"550e8400-e29b-41d4-a716-446655440014","nullable":true},"targetTodoId":{"type":"string","description":"Target todo ID (optional)","example":"550e8400-e29b-41d4-a716-446655440015","nullable":true},"targetTestCaseId":{"type":"string","description":"Target test case ID (optional)","example":"550e8400-e29b-41d4-a716-446655440016","nullable":true},"targetValue":{"type":"string","description":"Target value (optional)","example":"option-value","nullable":true},"containsType":{"type":"string","description":"Contains type","enum":["Some","All"],"example":"Some"}},"required":["type","containsType"]},"CreateLibraryComponentItemDto":{"type":"object","properties":{"componentId":{"type":"string","description":"Parent component ID","example":"550e8400-e29b-41d4-a716-446655440000"},"parentId":{"type":"string","description":"Parent item ID (optional, for nested items)","example":null,"nullable":true},"type":{"type":"string","description":"Type of component item","enum":["Epic","WorkPackage","TodoList","TestSuite"],"example":"Epic"},"name":{"type":"string","description":"Name of the component item","example":"User Authentication Epic"},"icon":{"type":"object","description":"Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:check:`, `:star:`). Optional.","example":":check:","nullable":true},"timeFrame":{"type":"number","description":"Time frame in hours (required for all types except Epic)","example":20},"description":{"type":"string","description":"Description in HTML or Markdown format (will be converted to IDoc)","example":"<p>Epic description in HTML</p>"},"tags":{"description":"Array of tag UUIDs (optional)","example":["550e8400-e29b-41d4-a716-446655440020"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML or Markdown format (optional, will be converted to IDoc)","example":"<p>Internal implementation notes</p>","nullable":true},"includeInSpecification":{"type":"boolean","description":"Whether to include in specification","example":true},"conditions":{"description":"Array of conditions (optional)","type":"array","items":{"$ref":"#/components/schemas/SaveComponentItemConditionDto"}}},"required":["componentId","type","name","description","includeInSpecification"]},"CreateLibraryComponentItemResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Success status","example":true},"id":{"type":"string","description":"Created item ID","example":"550e8400-e29b-41d4-a716-446655440001"}},"required":["success","id"]},"ComponentTreeComponentDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the component","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Name of the component","example":"User Authentication Module"},"icon":{"type":"object","description":"Icon identifier for the component (nullable). Format: :icon_name: (e.g., :rocket:, :box:, :star:)","example":":box:","nullable":true},"totalTimeFrame":{"type":"number","description":"Total time frame for the component","example":40}},"required":["id","name","icon","totalTimeFrame"]},"ComponentTreeItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the component item","example":"550e8400-e29b-41d4-a716-446655440001"},"name":{"type":"string","description":"Name of the component item","example":"Login Feature"},"icon":{"type":"object","description":"Icon identifier for the component item (nullable). Format: :icon_name: (e.g., :rocket:, :check:, :star:)","example":":check:","nullable":true},"type":{"type":"string","description":"Type of the component item. Items are the building blocks within components:\n- **Epic**: Groups related work packages around a common theme\n- **WorkPackage**: Self-contained task that delivers a tangible result\n- **TodoList**: Checklist of sub-steps or checkpoints\n- **TestSuite**: Formal acceptance tests with test cases","enum":["Epic","WorkPackage","TodoList","TestSuite"],"example":"Epic"},"componentId":{"type":"string","description":"ID of the parent component","example":"550e8400-e29b-41d4-a716-446655440000"},"parentId":{"type":"object","description":"ID of the parent item (nullable for top-level items)","example":null,"nullable":true},"conditionsValid":{"type":"boolean","description":"Whether all conditions are valid for this item","example":true},"failedConditionsIds":{"description":"Array of failed condition IDs","example":[],"type":"array","items":{"type":"string"}},"totalEstimate":{"type":"number","description":"Total time estimate for the item","example":20}},"required":["id","name","icon","type","componentId","parentId","conditionsValid","failedConditionsIds","totalEstimate"]},"ComponentLibraryTreeResponseDto":{"type":"object","properties":{"components":{"description":"List of components in the library","type":"array","items":{"$ref":"#/components/schemas/ComponentTreeComponentDto"}},"items":{"description":"List of items (Epics, Work Packages, Todo Lists, Test Suites) in the library. Items are the building blocks within components and can be nested to any depth.","type":"array","items":{"$ref":"#/components/schemas/ComponentTreeItemDto"}}},"required":["components","items"]},"ComponentChildrenShortItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"550e8400-e29b-41d4-a716-446655440001"},"name":{"type":"string","description":"Name of the item","example":"Login Feature"},"icon":{"type":"object","description":"Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:check:`, `:star:`). Can be null if no icon is set.","example":":check:","nullable":true},"type":{"type":"string","description":"Type of the component item. Items are the building blocks within components:\n- **Epic**: Groups related work packages around a common theme\n- **WorkPackage**: Self-contained task that delivers a tangible result\n- **TodoList**: Checklist of sub-steps or checkpoints\n- **TestSuite**: Formal acceptance tests with test cases","enum":["Epic","WorkPackage","TodoList","TestSuite"],"example":"Epic"},"conditionsValid":{"type":"boolean","description":"Whether all conditions are valid","example":true},"entityUniqueId":{"type":"string","description":"Entity unique ID","example":"unique-id-123"},"taskId":{"type":"object","description":"Task ID if linked to a task","example":"550e8400-e29b-41d4-a716-446655440002","nullable":true},"taskShortNumber":{"type":"object","description":"Task short number if linked to a task","example":123,"nullable":true},"testCasesCount":{"type":"number","description":"Count of test cases","example":3},"todosCount":{"type":"number","description":"Count of todo items","example":5},"questionsCount":{"type":"number","description":"Count of questions","example":2},"children":{"description":"Nested children items","type":"array","items":{"$ref":"#/components/schemas/ComponentChildrenShortItemDto"}}},"required":["id","name","icon","type","conditionsValid","entityUniqueId","taskId","taskShortNumber","testCasesCount","todosCount","questionsCount","children"]},"ComponentDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the component","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Name of the component","example":"User Authentication Module"},"projectId":{"type":"object","description":"Project ID (null for library components)","example":null,"nullable":true},"icon":{"type":"object","description":"Icon identifier (nullable)","example":"faBox","nullable":true},"description":{"type":"string","description":"Component description in HTML format","example":"<p>This module handles user authentication and authorization.</p>"},"tags":{"description":"Array of tags","example":["authentication","security"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML format","example":"<p>Internal implementation notes.</p>"},"children":{"description":"Array of direct children items","type":"array","items":{"$ref":"#/components/schemas/ComponentChildrenShortItemDto"}},"discountAmount":{"type":"number","description":"Discount amount applied to this component. 0 means no discount.","example":10},"discountType":{"type":"string","description":"Type of discount: \"Fixed\" (fixed amount) or \"Percentage\" (percentage of total price)","enum":["Fixed","Percentage"],"example":"Percentage"}},"required":["id","name","projectId","icon","description","tags","internalNote","children","discountAmount","discountType"]},"PatchComponentDto":{"type":"object","properties":{"name":{"type":"string","description":"Component name","example":"User Authentication Module"},"description":{"type":"string","description":"Component description (HTML or Markdown)","example":"<p>This component handles user authentication and authorization.</p>"},"icon":{"type":"object","description":"Icon identifier (can be set to null). Format: :icon_name: (e.g., :rocket:, :box:, :star:)","example":":box:","nullable":true},"tags":{"description":"Array of tag IDs","example":["550e8400-e29b-41d4-a716-446655440000"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note (HTML or Markdown)","example":"<p>Implementation notes for the development team.</p>"}}},"ComponentTestCaseDto":{"type":"object","properties":{"id":{"type":"string","description":"Test case ID","example":"550e8400-e29b-41d4-a716-446655440010"},"title":{"type":"string","description":"Test case title","example":"Test login with valid credentials"},"description":{"type":"string","description":"Test case description in HTML format","example":"<p>Verify that users can log in with valid credentials.</p>"},"steps":{"type":"string","description":"Test steps in HTML format","example":"<ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>"},"expectedResult":{"type":"string","description":"Expected result in HTML format","example":"<p>User is successfully logged in and redirected to dashboard.</p>"},"testComment":{"type":"string","description":"Test comment in HTML format (optional)","example":"<p>Test passed successfully.</p>","nullable":true},"testStatus":{"type":"string","description":"Test status (optional)","enum":["Passed","PassedWithReservations","Failed"],"example":"Passed","nullable":true},"testedBy":{"type":"string","description":"User ID who tested (optional)","example":"550e8400-e29b-41d4-a716-446655440020","nullable":true},"testedAt":{"type":"string","description":"Date when tested (optional)","example":"2024-01-01T00:00:00.000Z","nullable":true}},"required":["id","title","description","steps","expectedResult"]},"ComponentTodoItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Todo item ID","example":"550e8400-e29b-41d4-a716-446655440011"},"title":{"type":"string","description":"Todo item title. Todos are checklist items used to track actionable tasks within component items. They are perfect for recurring processes, quality control checkpoints, and project preparation steps.","example":"Implement password reset"},"description":{"type":"string","description":"Todo item description in HTML format","example":"<p>Add password reset functionality to the authentication module.</p>"},"isDone":{"type":"boolean","description":"Whether the todo is marked as done","example":false},"doneBy":{"type":"object","description":"User ID who marked as done (nullable)","example":"550e8400-e29b-41d4-a716-446655440020","nullable":true},"doneUpdatedAt":{"type":"object","description":"Date when marked as done (nullable)","example":"2024-01-01T00:00:00.000Z","nullable":true},"todoComment":{"type":"object","description":"Todo comment in HTML format (nullable)","example":"<p>Completed ahead of schedule.</p>","nullable":true}},"required":["id","title","description","isDone","doneBy","doneUpdatedAt","todoComment"]},"ComponentQuestionOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Option ID","example":"550e8400-e29b-41d4-a716-446655440012"},"title":{"type":"string","description":"Option title","example":"Two-factor authentication"},"extraHours":{"type":"number","description":"Extra hours for this option (optional)","example":8,"nullable":true},"isSelected":{"type":"boolean","description":"Whether this option is selected (optional)","example":true,"nullable":true}},"required":["id","title"]},"ComponentQuestionDto":{"type":"object","properties":{"id":{"type":"string","description":"Question ID","example":"550e8400-e29b-41d4-a716-446655440017"},"title":{"type":"string","description":"Question title","example":"Security Features"},"question":{"type":"string","description":"Question text","example":"Which security features should be implemented?"},"description":{"type":"string","description":"Question description in HTML format","example":"<p>Select all security features that apply.</p>"},"type":{"type":"string","description":"Question type","enum":["ShortText","Editor","Checkbox","Radio","Files","Datepicker","Multiplier","Person"],"example":"Checkbox"},"options":{"description":"Available options","type":"array","items":{"$ref":"#/components/schemas/ComponentQuestionOptionDto"}},"editorAnswer":{"type":"string","description":"Editor answer in HTML format (optional)","example":"<p>Two-factor authentication is required.</p>","nullable":true},"isAnswered":{"type":"boolean","description":"Whether the question has been answered","example":true},"answeredBy":{"type":"string","description":"User ID who answered (optional)","example":"550e8400-e29b-41d4-a716-446655440020","nullable":true},"answeredAt":{"type":"string","description":"Date when answered (optional)","example":"2024-01-01T00:00:00.000Z","nullable":true}},"required":["id","title","question","description","type","options","isAnswered"]},"ComponentItemConditionDto":{"type":"object","properties":{"id":{"type":"string","description":"Condition ID (optional)","example":"550e8400-e29b-41d4-a716-446655440013","nullable":true},"type":{"type":"string","description":"Condition type","enum":["QuestionAnswered","QuestionNotAnswered","QuestionAnswerContains","QuestionAnswerDoesNotContain","QuestionAnswerEqual","QuestionAnswerDoesNotEqual","TodoIsDone","TodoIsNotDone","TestCasePassed","TestCaseFailed"],"example":"QuestionAnswered"},"targetQuestionId":{"type":"string","description":"Target question ID (optional)","example":"550e8400-e29b-41d4-a716-446655440014","nullable":true},"targetTodoId":{"type":"string","description":"Target todo ID (optional)","example":"550e8400-e29b-41d4-a716-446655440015","nullable":true},"targetTestCaseId":{"type":"string","description":"Target test case ID (optional)","example":"550e8400-e29b-41d4-a716-446655440016","nullable":true},"targetValue":{"type":"string","description":"Target value (optional)","example":"option-value","nullable":true},"containsType":{"type":"string","description":"Contains type","enum":["Some","All"],"example":"Some"}},"required":["type","containsType"]},"ComponentItemDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Item ID","example":"550e8400-e29b-41d4-a716-446655440001"},"entityUniqueId":{"type":"string","description":"Entity unique ID","example":"unique-id-123"},"name":{"type":"string","description":"Item name","example":"Login Feature"},"icon":{"type":"object","description":"Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:check:`, `:star:`). Can be null if no icon is set.","example":":check:","nullable":true},"description":{"type":"string","description":"Item description in HTML format","example":"<p>Feature for user authentication.</p>"},"tags":{"description":"Array of tags","example":["authentication","login"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML format","example":"<p>Implementation notes.</p>"},"includeInSpecification":{"type":"boolean","description":"Whether to include in specification","example":true},"componentId":{"type":"string","description":"Parent component ID","example":"550e8400-e29b-41d4-a716-446655440000"},"parentId":{"type":"object","description":"Parent item ID (nullable for top-level items)","example":null,"nullable":true},"type":{"type":"string","description":"Item type","enum":["Epic","WorkPackage","TodoList","TestSuite"],"example":"Epic"},"testCases":{"description":"Test cases associated with this item. Test cases are individual test steps within a Test Suite that document formal review and approval criteria.","type":"array","items":{"$ref":"#/components/schemas/ComponentTestCaseDto"}},"todoItems":{"description":"Todo items associated with this item. Todos are checklist items that can be checked off individually and support comments.","type":"array","items":{"$ref":"#/components/schemas/ComponentTodoItemDto"}},"questions":{"description":"Questions associated with this item. Questions capture customer requirements and can affect effort calculation. In library components, questions are defined but not answered.","type":"array","items":{"$ref":"#/components/schemas/ComponentQuestionDto"}},"conditions":{"description":"Conditions for this item","type":"array","items":{"$ref":"#/components/schemas/ComponentItemConditionDto"}},"conditionsValid":{"type":"boolean","description":"Whether all conditions are valid","example":true},"failedConditionsIds":{"description":"Array of failed condition IDs","example":[],"type":"array","items":{"type":"string"}},"timeFrame":{"type":"number","description":"Time frame in hours","example":40},"children":{"description":"Nested children items","type":"array","items":{"$ref":"#/components/schemas/ComponentChildrenShortItemDto"}},"taskId":{"type":"object","description":"Task ID if linked to a task","example":"550e8400-e29b-41d4-a716-446655440002","nullable":true},"taskShortNumber":{"type":"object","description":"Task short number if linked to a task","example":123,"nullable":true}},"required":["id","entityUniqueId","name","icon","description","tags","internalNote","includeInSpecification","componentId","parentId","type","testCases","todoItems","questions","conditions","conditionsValid","failedConditionsIds","timeFrame","children","taskId","taskShortNumber"]},"CreateComponentTodoDto":{"type":"object","properties":{"title":{"type":"string","description":"Todo title","example":"Implement authentication"},"description":{"type":"string","description":"Todo description in HTML or Markdown format","example":"<p>Add user login functionality</p>"}},"required":["title","description"]},"TodoOperationResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Operation success status","example":true},"id":{"type":"string","description":"ID of the updated todo","example":"550e8400-e29b-41d4-a716-446655440001"}},"required":["success"]},"UpdateComponentTodoDto":{"type":"object","properties":{"title":{"type":"string","description":"Todo title","example":"Implement authentication with 2FA"},"description":{"type":"string","description":"Todo description in HTML or Markdown format","example":"<p>Add user login with two-factor authentication</p>"}}},"SortComponentTodosDto":{"type":"object","properties":{"newSortOrder":{"description":"Array of todo IDs in the new sort order","example":["550e8400-e29b-41d4-a716-446655440001","550e8400-e29b-41d4-a716-446655440002"],"type":"array","items":{"type":"string"}}},"required":["newSortOrder"]},"CreateComponentTestCaseDto":{"type":"object","properties":{"title":{"type":"string","description":"Test case title. Test cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results.","example":"Test login with valid credentials"},"description":{"type":"string","description":"Test case description in HTML or Markdown format","example":"<p>Verify that users can log in with valid credentials.</p>"},"steps":{"type":"string","description":"Test steps in HTML or Markdown format","example":"<ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>"},"expectedResult":{"type":"string","description":"Expected result in HTML or Markdown format","example":"<p>User is successfully logged in and redirected to dashboard.</p>"}},"required":["title","description","steps","expectedResult"]},"ComponentTestCaseResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Test case ID","example":"550e8400-e29b-41d4-a716-446655440010"},"title":{"type":"string","description":"Test case title","example":"Test login with valid credentials"},"description":{"type":"string","description":"Test case description in HTML format","example":"<p>Verify that users can log in with valid credentials.</p>"},"steps":{"type":"string","description":"Test steps in HTML format","example":"<ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>"},"expectedResult":{"type":"string","description":"Expected result in HTML format","example":"<p>User is successfully logged in and redirected to dashboard.</p>"},"sort":{"type":"number","description":"Sort order","example":1},"testComment":{"type":"string","description":"Test comment in HTML format (optional)","example":"<p>Test passed successfully.</p>","nullable":true},"testStatus":{"type":"string","description":"Test status (optional)","enum":["Passed","PassedWithReservations","Failed"],"example":"Passed","nullable":true},"testedBy":{"type":"string","description":"User ID who tested (optional)","example":"550e8400-e29b-41d4-a716-446655440020","nullable":true},"testedAt":{"type":"string","description":"Date when tested (optional)","example":"2024-01-01T00:00:00.000Z","nullable":true}},"required":["id","title","description","steps","expectedResult","sort"]},"UpdateComponentTestCaseDto":{"type":"object","properties":{"title":{"type":"string","description":"Test case title","example":"Test login with valid credentials"},"description":{"type":"string","description":"Test case description in HTML or Markdown format","example":"<p>Verify that users can log in with valid credentials.</p>"},"steps":{"type":"string","description":"Test steps in HTML or Markdown format","example":"<ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>"},"expectedResult":{"type":"string","description":"Expected result in HTML or Markdown format","example":"<p>User is successfully logged in and redirected to dashboard.</p>"}}},"SortComponentTestCasesDto":{"type":"object","properties":{"newSortOrder":{"description":"Array of testcase IDs in desired order","example":["550e8400-e29b-41d4-a716-446655440010","550e8400-e29b-41d4-a716-446655440011","550e8400-e29b-41d4-a716-446655440012"],"type":"array","items":{"type":"string"}}},"required":["newSortOrder"]},"PatchLibraryComponentItemDto":{"type":"object","properties":{"componentId":{"type":"string","description":"ID of the component this item belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"parentId":{"type":"string","description":"ID of parent item for nested items","example":"550e8400-e29b-41d4-a716-446655440000"},"type":{"type":"string","description":"Type of item (Epic, WorkPackage, TodoList, TestSuite)","example":"WorkPackage","enum":["Epic","WorkPackage","TodoList","TestSuite"]},"name":{"type":"string","description":"Name of the item","example":"User Authentication Feature"},"icon":{"type":"object","description":"Icon identifier (can be set to null). Format: :icon_name: (e.g., :rocket:, :check:, :star:)","example":":check:","nullable":true},"timeFrame":{"type":"number","description":"Time frame in hours. Required for all types except Epic (when type is provided)","example":40},"description":{"type":"string","description":"Description in HTML or Markdown format (will be converted to IDoc if provided)","example":"<p>Implement user authentication and authorization</p>"},"tags":{"description":"Array of tag UUIDs","example":["550e8400-e29b-41d4-a716-446655440000"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML or Markdown format (will be converted to IDoc if provided)","example":"<p>Implementation notes for the development team</p>"},"includeInSpecification":{"type":"boolean","description":"Whether to include in specification","example":true},"conditions":{"description":"Array of conditions (full array replaces existing conditions)","type":"array","items":{"$ref":"#/components/schemas/SaveComponentItemConditionDto"}}}},"DeleteComponentItemResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Success status","example":true}},"required":["success"]},"SaveComponentsSortDto":{"type":"object","properties":{}},"DuplicateComponentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the newly created duplicate component","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}},"required":["id"]},"TagResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier (UUID) of the tag. Use this ID when updating or deleting the tag.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"The display name of the tag. This is what users see when the tag is applied to tasks, projects, or components.","example":"urgent"},"color":{"type":"string","description":"The predefined color code assigned to the tag for visual organization. Null if no color has been set. Colors help users quickly identify tags in lists and cards.\n\n**Available color values:**\n- `default` - Light gray (default color)\n- `red` - Red\n- `yellow` - Yellow\n- `green` - Green\n- `blue` - Blue\n- `purple` - Purple\n- `fuchsia` - Fuchsia\n- `pink` - Pink\n- `magenta` - Magenta","enum":["default","red","yellow","green","blue","purple","fuchsia","pink","magenta",null],"example":"blue","nullable":true},"isDeleted":{"type":"boolean","description":"Indicates whether the tag has been soft-deleted. Soft-deleted tags are marked as deleted but not permanently removed from the database. They will not appear in list endpoints and are automatically removed from all associated entities.","example":false}},"required":["id","name","color","isDeleted"]},"CreateTagDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the tag to create. Must be unique within the tag type (case-insensitive). If a tag with this name already exists, the existing tag will be returned instead of creating a duplicate. Use descriptive names that clearly indicate the topic or process (e.g., \"Billing\", \"Support\", \"Onboarding\").","example":"urgent"}},"required":["name"]},"SaveTagDto":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier (UUID) of the tag to update. This identifies which existing tag you want to modify.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"The new name for the tag. Must be unique within the tag type (case-insensitive). If you change the name, all entities using this tag will automatically reflect the new name.","example":"urgent"},"color":{"type":"string","description":"Optional predefined color code for visual organization of the tag. Must be one of the predefined color values from the palette. Colors help visually distinguish tags in lists and cards. If not provided, the tag will have no color.\n\n**Available color values:**\n- `default` - Light gray (default color)\n- `red` - Red\n- `yellow` - Yellow\n- `green` - Green\n- `blue` - Blue\n- `purple` - Purple\n- `fuchsia` - Fuchsia\n- `pink` - Pink\n- `magenta` - Magenta","enum":["default","red","yellow","green","blue","purple","fuchsia","pink","magenta"],"example":"blue"}},"required":["id","name"]},"AttachTaskFormInstanceDto":{"type":"object","properties":{"templateId":{"type":"string","format":"uuid","description":"ID of the form template to attach. List templates (each includes `id`) via GET `/workspace/form-templates` or GET `/administration/form-templates-available`. Optional: inspect field definitions before attach with GET `/workspace/form-templates/{id}`."}},"required":["templateId"]},"PatchTaskFormInstanceDto":{"type":"object","properties":{"values":{"type":"object","additionalProperties":true,"description":"Map of field keys to submitted values. Each key must match the `valueKey` of a field on this instance (see GET `/tasks/{identifier}/form-instances` → `fields[].valueKey`). `valueKey` is the template field `key` when set, otherwise the field UUID. To learn types, required flags, and allowed options before editing, use GET `/workspace/form-templates/{templateId}` (use `templateId` from the instance) or read `fields` from the list response."}},"required":["values"]},"ProductSettingOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the product option. This identifies which option (e.g., \"Color\", \"Size\", \"Support Level\") is being configured.","example":"d8d9f2c3-5e8a-4f6b-9c7d-3e4f5a6b7c8d"},"value":{"description":"Array of UUIDs representing the selected option values. For example, if the option is \"Color\", this array might contain IDs for \"Blue\" and \"Red\" if multiple selections are allowed. Each value ID must be a valid UUID.","example":["550e8400-e29b-41d4-a716-446655440005"],"type":"array","items":{"type":"string"}}},"required":["id","value"]},"ImportProductToTaskDto":{"type":"object","properties":{"productId":{"type":"string","description":"UUID of the product from your workspace product catalog. The product must exist in the catalog and not be already assigned to a task or project. This product will be copied into the task.","example":"550e8400-e29b-41d4-a716-446655440002"},"quantity":{"type":"number","description":"Number of units of this product to add to the task. Must be at least 1. For example, if importing a \"Workshop\" product, quantity 2 means two workshops.","example":2,"minimum":1},"activeVariantId":{"type":"object","description":"UUID of the variant to activate when importing. Optional - only needed if the product has multiple variants (e.g., Standard, Pro, Enterprise). If not provided, the first variant will be active by default.","example":"550e8400-e29b-41d4-a716-446655440003","nullable":true},"options":{"description":"Array of option selections for this product. Each option specifies which values are selected. For example, if a product has a \"Support Level\" option with values \"Basic\" and \"Premium\", you would include the option ID and select one or more value IDs. Required even if empty array.","example":[{"id":"550e8400-e29b-41d4-a716-446655440004","value":["550e8400-e29b-41d4-a716-446655440005"]}],"type":"array","items":{"$ref":"#/components/schemas/ProductSettingOptionDto"}},"activeFromType":{"type":"string","description":"Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.","enum":["Immediately","ProjectBilling","FixedDate"],"example":"Immediately"},"activeFromFixedDate":{"type":"string","description":"Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.","example":"2024-01-01","nullable":true,"format":"date"},"customPrices":{"type":"object","description":"Custom price overrides for base product, variants, and options. Keys can be \"base\", \"variant_<variantId>\", or \"option_<optionValueId>\". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving. Note: Tasks only support priceFixed, so priceSubscription and pricePerUnit are ignored.","additionalProperties":{"$ref":"#/components/schemas/CustomPriceDto"},"example":{"base":{"priceFixed":100.5,"priceSubscription":null,"pricePerUnit":null},"variant_550e8400":{"priceFixed":150,"priceSubscription":null,"pricePerUnit":null}}}},"required":["productId","quantity","options","customPrices"]},"TaskProductVariantDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the variant. Required when updating an existing variant. Omit or leave undefined when creating a new variant.","example":"550e8400-e29b-41d4-a716-446655440006"},"name":{"type":"string","description":"Display name of the variant. Examples: \"Standard\", \"Pro\", \"Enterprise\", \"Basic Package\", \"Premium Package\".","example":"Premium"},"description":{"type":"object","description":"Detailed description of the variant in HTML or Markdown format. This will be automatically converted to the internal document format. Can describe features, specifications, or what is included in this variant.","example":"<p>Premium variant with additional features</p>"},"priceFixed":{"type":"object","description":"One-time fixed price for this variant. Task products only support fixed pricing. Set to null if this pricing model does not apply. Must be 0 or greater.","example":150,"nullable":true}},"required":["name"]},"TaskProductOptionValueDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the option value. Required when updating an existing value. Omit or leave undefined when creating a new value.","example":"550e8400-e29b-41d4-a716-446655440008"},"name":{"type":"string","description":"Display name of the option value. Examples: \"Blue\", \"Red\", \"Premium Support\", \"Add Workshop\", \"Extra Storage\".","example":"Blue"},"extraPriceFixed":{"type":"object","description":"Additional fixed price charged when this option value is selected. Task products only support fixed pricing. For example, if selecting \"Premium Support\" adds 50 EUR one-time fee, set this to 50.0. Set to null if no extra fixed price applies. Must be 0 or greater.","example":10,"nullable":true}},"required":["name"]},"TaskProductOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the product option. Required when updating an existing option. Omit or leave undefined when creating a new option.","example":"550e8400-e29b-41d4-a716-446655440007"},"name":{"type":"string","description":"Display name of the option. Examples: \"Color\", \"Size\", \"Support Level\", \"Additional Services\". This identifies what type of choice the customer is making.","example":"Color"},"isRequired":{"type":"boolean","description":"Whether this option must be selected. If true, customers must choose at least one value from this option. If false, the option is optional.","example":true},"isMultiple":{"type":"boolean","description":"Whether multiple values can be selected for this option. If true, customers can select multiple values (e.g., multiple colors). If false, only one value can be selected.","example":false},"values":{"description":"Array of available values for this option. Each value can have its own name and fixed pricing. Task products only support fixed pricing. For example, a \"Support Level\" option might have values: \"Basic\" (no extra cost), \"Premium\" (+50 EUR one-time), \"Enterprise\" (+200 EUR one-time).","type":"array","items":{"$ref":"#/components/schemas/TaskProductOptionValueDto"}}},"required":["name","isRequired","isMultiple","values"]},"PatchTaskProductDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name of the product. This is what appears in quotes, invoices, and task views. All fields in this DTO are optional - only provide fields you want to update.","example":"Updated Product"},"description":{"type":"string","description":"Product description in HTML or Markdown format. This will be automatically converted to the internal document format. Can include details about what the product includes, features, or specifications.","example":"<p>Updated product description</p>"},"categoryId":{"type":"object","description":"UUID of the product category. Categories help organize products (e.g., \"Hardware\", \"Software\", \"Services\"). Set to null to remove category assignment.","example":"550e8400-e29b-41d4-a716-446655440010","nullable":true},"logoId":{"type":"object","description":"UUID of the logo image file. This image is displayed alongside the product in lists and views. Set to null to remove the logo.","example":"550e8400-e29b-41d4-a716-446655440011","nullable":true},"priceFixed":{"type":"object","description":"One-time fixed price for the product. Task products only support fixed pricing. Set to null if this pricing model does not apply. Must be 0 or greater.","example":100,"nullable":true},"variants":{"description":"Array of product variants. Variants allow different configurations or service levels (e.g., Standard, Pro, Enterprise). When updating, you must provide the complete variant structure - existing variants will be replaced. Task products only support fixed pricing.","type":"array","items":{"$ref":"#/components/schemas/TaskProductVariantDto"}},"options":{"description":"Array of product options. Options allow customers to add extra services or upgrades (e.g., \"Add workshop\", \"Enable premium support\"). When updating, you must provide the complete options structure - existing options will be replaced. Task products only support fixed pricing.","type":"array","items":{"$ref":"#/components/schemas/TaskProductOptionDto"}}}},"TaskProductSettingsDto":{"type":"object","properties":{"quantity":{"type":"number","description":"Number of units of this product. Must be at least 1. This affects the total price calculation when using per-unit pricing.","example":3,"minimum":1},"activeVariantId":{"type":"object","description":"UUID of the variant that should be active. Use this to switch between variants (e.g., from Standard to Pro). Set to null if the product has no variants or to deactivate variant selection.","example":"550e8400-e29b-41d4-a716-446655440006","nullable":true},"options":{"description":"Array of option selections. Each entry specifies which option is being configured and which values are selected. You must provide selections for all options defined on the product, even if no values are selected (use empty array for value). This updates the product configuration without changing product details like name or prices.","example":[{"id":"550e8400-e29b-41d4-a716-446655440004","value":["550e8400-e29b-41d4-a716-446655440007"]}],"type":"array","items":{"$ref":"#/components/schemas/ProductSettingOptionDto"}},"activeFromType":{"type":"string","description":"Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.","enum":["Immediately","ProjectBilling","FixedDate"],"example":"Immediately"},"activeFromFixedDate":{"type":"string","description":"Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.","example":"2024-01-01","nullable":true,"format":"date"},"customPrices":{"type":"object","description":"Custom price overrides for base product, variants, and options. Keys can be \"base\", \"variant_<variantId>\", or \"option_<optionValueId>\". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving. Note: Tasks only support priceFixed, so priceSubscription and pricePerUnit are ignored.","additionalProperties":{"$ref":"#/components/schemas/CustomPriceDto"},"example":{"base":{"priceFixed":100.5,"priceSubscription":null,"pricePerUnit":null},"variant_550e8400":{"priceFixed":150,"priceSubscription":null,"pricePerUnit":null}}}},"required":["quantity","options","customPrices"]},"CreateExpressQuotationDto":{"type":"object","properties":{"contactUserId":{"type":"string","description":"UUID of the contact user who will receive the quotation. This user must exist in the workspace and will be the recipient when the quotation is sent via email.","example":"550e8400-e29b-41d4-a716-446655440000"},"language":{"type":"string","description":"Language code for the quotation (e.g., \"en\" for English, \"de\" for German). Used for formatting dates, numbers, and generating localized item descriptions. If not provided, the workspace default language is used.","example":"en"},"comment":{"type":"string","description":"Optional comment or notes for the quotation. Can be provided in HTML or Markdown format. The system will automatically detect the format and convert it to the internal IDoc format. This comment appears on the quotation document when sent to the contact.","example":"<p>Please review this quotation and let us know if you have any questions.</p>"},"includeEstimate":{"type":"boolean","description":"If true, includes the task's estimated work time as the first quotation item. The estimated time is converted to an item with the task's hour rate. If false, only products from the task are included as items.","example":true}},"required":["contactUserId","includeEstimate"]},"ExpressQuotationItemOptionResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the option (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","description":"Display name or title of the option variant (e.g., \"Color: Red\", \"Size: Large\", \"Material: Premium\"). Options represent different variants or configurations of a quotation item.","example":"Color: Red"},"unitPrice":{"type":"number","description":"Price per unit for this option variant. This is the additional cost per unit when this option is selected.","example":5.99},"totalPrice":{"type":"number","description":"Total price for this option variant, calculated as `unitPrice * itemQuantity`. This represents the total additional cost when this option is selected for the item.","example":59.9}},"required":["id","title","unitPrice","totalPrice"]},"ExpressQuotationItemResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the quotation item (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","description":"Display title of the item. For time-based items, this typically includes the hours and rate (e.g., \"Work time: 1440 hours at 100 EUR each\"). For product items, this is the product name.","example":"Work time: 1440 hours at 100 EUR each"},"description":{"type":"string","description":"Detailed description of the item in HTML format. For time-based items, this may include the task summary. For product items, this is the product description. The content is converted from the internal IDoc format to HTML.","example":"<p>Development and implementation of the new feature as described in the task requirements.</p>"},"unit":{"type":"string","description":"Unit of measurement for this item. Can be \"Hour\" for time-based billing or \"Piece\" for product-based items.","enum":["Piece","Hour"],"example":"Hour"},"quantity":{"type":"number","description":"Quantity of units for this item. For time-based items, this is the number of hours. For product items, this is the product quantity.","example":1440},"unitPrice":{"type":"number","description":"Price per unit for this item. For time-based items, this is the hourly rate. For product items, this is the product unit price.","example":100},"totalPrice":{"type":"number","description":"Total price for this item, calculated as `quantity * unitPrice`. This does not include option prices, which are added separately.","example":144000},"options":{"description":"Array of option variants available for this item. Options represent different configurations or variants (e.g., color, size, material) that can be selected, each with its own pricing. An empty array means no options are available.","type":"array","items":{"$ref":"#/components/schemas/ExpressQuotationItemOptionResponseDto"}}},"required":["id","title","description","unit","quantity","unitPrice","totalPrice","options"]},"ExpressQuotationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the quotation (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"taskId":{"type":"string","description":"UUID of the task this quotation is linked to. The quotation affects billing for this task when accepted.","example":"550e8400-e29b-41d4-a716-446655440000"},"shortNumber":{"type":"number","description":"Sequential quotation number within the task. The first quotation for a task is 1, the second is 2, etc. This provides a human-readable reference number for the quotation.","example":1},"status":{"type":"string","description":"Current status of the quotation. \"Pending\" means it has been created but not yet accepted or rejected. \"Accepted\" means it will be used for billing. \"Rejected\" means it will not be used for billing. Only one quotation per task can be Accepted at a time.","enum":["Pending","Accepted","Rejected"],"example":"Pending"},"contactUserId":{"type":"string","description":"UUID of the contact user who is the recipient of this quotation. This user receives the quotation when it is sent via email.","example":"550e8400-e29b-41d4-a716-446655440000"},"language":{"type":"object","description":"Language code used for this quotation (e.g., \"en\", \"de\"). Used for formatting and localization. Can be null if not specified, in which case the workspace default is used.","example":"en","nullable":true},"comment":{"type":"string","description":"Comment or notes for the quotation, formatted as HTML. This content is converted from the internal IDoc format and appears on the quotation document when sent to the contact.","example":"<p>Please review this quotation and let us know if you have any questions.</p>"},"items":{"description":"Array of all items included in the quotation. Items are ordered by their `order` field. Each item can have multiple options (variants). Items are typically generated from task products and optionally include estimated work time.","type":"array","items":{"$ref":"#/components/schemas/ExpressQuotationItemResponseDto"}},"subTotal":{"type":"number","description":"Total amount for all items before tax is applied. This is the sum of all item total prices plus any option prices.","example":144000},"total":{"type":"number","description":"Final total amount including tax. Calculated as `subTotal + taxAmount`. This is the amount that will be billed if the quotation is accepted.","example":168480},"tax":{"type":"number","description":"Tax rate percentage applied to the quotation (e.g., 17 for 17% VAT). This is typically taken from the workspace or organization tax settings.","example":17},"taxAmount":{"type":"number","description":"Tax amount calculated from the subtotal and tax rate. Calculated as `(subTotal * tax) / 100`.","example":24480},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp when the quotation was created","example":"2024-01-01T00:00:00Z"},"updatedAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp when the quotation was last modified","example":"2024-01-01T00:00:00Z"},"sentAt":{"type":"object","description":"ISO 8601 timestamp when the quotation was sent via email to the contact user. Null if the quotation has not been sent yet.","example":"2024-01-01T00:00:00Z","nullable":true},"createdBy":{"type":"string","description":"UUID of the user who created this quotation","example":"550e8400-e29b-41d4-a716-446655440000"},"statusChangedBy":{"type":"string","description":"UUID of the user who last changed the quotation status (accepted or rejected). This tracks who made the decision on the quotation.","example":"550e8400-e29b-41d4-a716-446655440000"}},"required":["id","taskId","shortNumber","status","contactUserId","language","comment","items","subTotal","total","tax","taxAmount","createdAt","updatedAt","sentAt","createdBy","statusChangedBy"]},"BigPictureResponseDto":{"type":"object","properties":{"groups":{"description":"Array of task groups, where each group represents a task type (row) in the Big Picture view. Groups are sorted according to the configured task type order. Each group contains task items (cards) and statistics for that specific task type. Tasks without a recognized type are grouped into a special \"other\" category.","example":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","items":[{"id":"task-uuid-1","title":"Implement user authentication","status":"In Progress","priority":"High","typeId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","estimatedHours":8,"spentHours":4.5,"assignedUserId":"user-uuid-1","itemStatus":1,"isInPipeline":true,"lastComment":null}],"stats":{"resolvedCount":2,"totalEstimate":16,"totalSpent":8,"resolvedHours":4}}],"type":"array","items":{"type":"array"}},"stats":{"type":"object","description":"Overall statistics aggregated across all tasks in the project. These metrics provide a high-level view of project progress, time allocation, and completion status.","example":{"totalTasks":10,"resolvedCount":3,"totalEstimate":40,"totalSpent":20,"resolvedHours":10}}},"required":["groups","stats"]},"UpdateBigPictureOrderDto":{"type":"object","properties":{"projectId":{"type":"string","description":"UUID of the project containing the tasks to reorder. The project must exist, not be deleted, and belong to the authenticated user's workspace. The user must have BigPicture.canEdit permission and access to the project.","example":"7225cca9-ac5f-461d-9a9d-ad8d7ee1b597","format":"uuid"},"groupId":{"type":"string","description":"UUID of the task type group (row) within which to reorder tasks. This identifies which horizontal row in the Big Picture view contains the tasks you want to reorder. Must be a valid task type ID for the specified project.","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","format":"uuid"},"newOrder":{"description":"Array of task UUIDs in the desired new order (left to right = highest to lowest priority). Must include all tasks currently in the specified group - you cannot omit any tasks. The first task ID in the array will appear leftmost (highest priority), the last will appear rightmost (lowest priority).","example":["f1a2b3c4-d5e6-7890-abcd-ef1234567890","a1b2c3d4-e5f6-7890-abcd-ef1234567890","b1c2d3e4-f5a6-7890-abcd-ef1234567890"],"items":{"type":"array"},"type":"array"}},"required":["projectId","groupId","newOrder"]},"UpdateBigPictureGroupOrderDto":{"type":"object","properties":{"projectId":{"type":"string","description":"UUID of the project containing the task type groups to reorder. The project must exist, not be deleted, and belong to the authenticated user's workspace. The user must have BigPicture.canEdit permission and access to the project.","example":"7225cca9-ac5f-461d-9a9d-ad8d7ee1b597","format":"uuid"},"newOrder":{"description":"Array of task type group UUIDs in the desired new order (top to bottom). Must include all task type groups currently in the specified project - you cannot omit any groups. The first group ID in the array will appear at the top of the Big Picture view, the last will appear at the bottom. Tasks without a recognized type are grouped into a special \"other\" category which can also be included in this array.","example":["a1b2c3d4-e5f6-7890-abcd-ef1234567890","b1c2d3e4-f5a6-7890-abcd-ef1234567890","c1d2e3f4-a5b6-7890-abcd-ef1234567890"],"items":{"type":"array"},"type":"array"}},"required":["projectId","newOrder"]},"PoolResponseDto":{"type":"object","properties":{"stats":{"type":"object","description":"Overall statistics for the entire pool, aggregating data from all projects and tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included. These statistics provide a high-level overview of workload and progress.","example":{"totalTasks":10,"resolvedCount":3,"totalEstimate":40,"totalSpent":20,"resolvedHours":10}},"projects":{"type":"array","description":"Array of projects, each containing groups of tasks organized by task type. Tasks are filtered by the Accountable field (not the Assigned field) - only tasks where the accountableId user is set as the Accountable person are included. Projects are sorted by custom order from PoolProjectOrder table. Each project row contains multiple task type groups (e.g., Feature, Bug, Task), which are sorted by custom order from PoolTypeProjectOrder table. Each group contains task items and group-level statistics.","example":[{"projectId":"550e8400-e29b-41d4-a716-446655440000","groups":[{"id":"6ba7b810-9dad-11d1-80b4-00c04fd430c8","items":[],"stats":{"resolvedCount":2,"totalEstimate":16,"totalSpent":8,"resolvedHours":4}}]}]}},"required":["stats","projects"]},"UpdatePoolOrderDto":{"type":"object","properties":{"accountableId":{"type":"string","description":"UUID of the accountable user whose pool task order should be updated. The user must exist in the same workspace and not be deleted. Access is controlled by Pool edit permissions (canEditAll, canEditOwnTeams, or canEditOwn).","example":"aff55c87-49c6-4d5d-9b0b-bf7478e2d504"},"newOrder":{"description":"Array of task UUIDs in the desired priority order. Tasks are displayed horizontally from left to right, with the first item in the array appearing on the left (highest priority). The array should contain all task IDs that need to be reordered. Each task ID must be a valid UUID of a task where the specified accountableId user is set as the Accountable person (filtered by Accountable field, not Assigned field).","example":["550e8400-e29b-41d4-a716-446655440000","6ba7b810-9dad-11d1-80b4-00c04fd430c8","6ba7b811-9dad-11d1-80b4-00c04fd430c8"],"type":"array","items":{"type":"string"}}},"required":["accountableId","newOrder"]},"UpdatePoolProjectOrderDto":{"type":"object","properties":{"accountableId":{"type":"string","description":"UUID of the accountable user whose pool project order should be updated. The user must exist in the same workspace and not be deleted. Access is controlled by Pool edit permissions (canEditAll, canEditOwnTeams, or canEditOwn).","example":"aff55c87-49c6-4d5d-9b0b-bf7478e2d504"},"newOrder":{"description":"Array of project UUIDs in the desired vertical order. Projects are displayed as rows in the pool view, with the first item in the array appearing at the top. The array should contain all project IDs that need to be reordered. Each project ID must be a valid UUID of a project in the same workspace.","example":["550e8400-e29b-41d4-a716-446655440000","6ba7b810-9dad-11d1-80b4-00c04fd430c8","6ba7b811-9dad-11d1-80b4-00c04fd430c8"],"type":"array","items":{"type":"string"}}},"required":["accountableId","newOrder"]},"UpdatePoolGroupOrderDto":{"type":"object","properties":{"accountableId":{"type":"string","description":"UUID of the accountable user whose pool group order should be updated. The user must exist in the same workspace and not be deleted. Access is controlled by Pool edit permissions (canEditAll, canEditOwnTeams, or canEditOwn).","example":"aff55c87-49c6-4d5d-9b0b-bf7478e2d504"},"projectId":{"type":"string","description":"UUID of the project within which the task type group order should be updated. The project must exist in the same workspace. Task type groups (e.g., Feature, Bug, Task) are displayed as columns within each project row in the pool view.","example":"550e8400-e29b-41d4-a716-446655440000"},"newOrder":{"description":"Array of task type UUIDs in the desired horizontal order. Task type groups are displayed as columns within the project row, with the first item in the array appearing on the left. The array should contain all task type IDs that need to be reordered. Each task type ID must be a valid UUID of a task type configured in the workspace.","example":["550e8400-e29b-41d4-a716-446655440000","6ba7b810-9dad-11d1-80b4-00c04fd430c8","6ba7b811-9dad-11d1-80b4-00c04fd430c8"],"type":"array","items":{"type":"string"}}},"required":["accountableId","projectId","newOrder"]},"StackColumnType":{"type":"string","enum":["toDo","getFeedback","giveFeedback","done","backlog"],"description":"Type of column indicating the workflow stage. Possible values: toDo (pipeline tasks in New/InProgress status), giveFeedback (tasks assigned to user in Feedback status), getFeedback (tasks NOT assigned to user in Feedback status), done (pipeline tasks in Closed/Resolved status), backlog (non-pipeline tasks assigned to user). **Note**: Pipeline tasks appear regardless of assignment. Non-pipeline tasks are filtered by Assigned field."},"TaskPriority":{"type":"string","enum":["Low","Normal","High"],"description":"Priority level of the task. Used for sorting: High > Normal > Low. Tasks with higher priority appear first in their column."},"StackTaskDetailsDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the task"},"title":{"type":"string","description":"Title or name of the task"},"icon":{"type":"object","description":"Icon identifier for the task. Can be null if no icon is set.","nullable":true},"shortNumber":{"type":"number","description":"Short number of the task within its workspace. Used for display and sorting purposes."},"color":{"type":"string","description":"Color code (hex format) representing the organization that owns the task. Used for visual identification."},"organizationShortName":{"type":"string","description":"Short name or abbreviation of the organization that owns the task"},"projectShortNumber":{"type":"object","description":"Short number of the project this task belongs to. Can be null if task is not associated with a project.","nullable":true},"statusId":{"type":"string","description":"UUID of the task status. The status determines which column the task appears in (toDo, giveFeedback, getFeedback, done, backlog)."},"summary":{"type":"object","description":"Optional summary or description of the task. Provides additional context about what needs to be done.","nullable":true},"priority":{"description":"Priority level of the task. Used for sorting: High > Normal > Low. Tasks with higher priority appear first in their column.","allOf":[{"$ref":"#/components/schemas/TaskPriority"}]},"typeId":{"type":"string","description":"UUID of the task type. Task types define workflows, fields, and statuses available for the task."},"projectId":{"type":"string","description":"UUID of the project this task belongs to"},"deadline":{"type":"object","description":"Deadline date in ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss.sssZ). Can be null if no deadline is set.","nullable":true},"estimatedTime":{"type":"object","description":"Estimated time to complete the task, measured in hours. Can be null if no estimate is provided.","nullable":true},"spentTime":{"type":"object","description":"Total time already spent on the task, measured in hours. This field is only visible if the authenticated user has the Tasks.seeLoggedTime permission. Can be null if no time has been logged yet.","nullable":true},"commentsCount":{"type":"number","description":"Total number of comments on the task. Includes all comments from team members."},"accountableId":{"type":"object","description":"UUID of the user who is accountable for the task (responsible person). Can be null if no one is assigned as accountable.","nullable":true},"assignedToId":{"type":"object","description":"UUID of the user who is assigned to work on the task (Assigned field). Can be null if no one is assigned. **Important**: Non-pipeline tasks in the stack are filtered by this field (assignedToId). Pipeline tasks appear regardless of this field. This field determines which column the task appears in for feedback-related columns (giveFeedback vs getFeedback).","nullable":true},"orgName":{"type":"string","description":"Full name of the organization that owns the task. Provides human-readable context."},"tags":{"description":"Array of tag names associated with the task. Tags enable cross-project organization and filtering of tasks.","type":"array","items":{"type":"string"}}},"required":["id","title","icon","shortNumber","color","organizationShortName","projectShortNumber","statusId","summary","priority","typeId","projectId","deadline","estimatedTime","commentsCount","accountableId","assignedToId","orgName","tags"]},"StackColumnDto":{"type":"object","properties":{"type":{"description":"Type of column indicating the workflow stage. Possible values: toDo (pipeline tasks in New/InProgress status), giveFeedback (tasks assigned to user in Feedback status), getFeedback (tasks NOT assigned to user in Feedback status), done (pipeline tasks in Closed/Resolved status), backlog (non-pipeline tasks assigned to user). **Note**: Pipeline tasks appear regardless of assignment. Non-pipeline tasks are filtered by Assigned field.","allOf":[{"$ref":"#/components/schemas/StackColumnType"}]},"tasks":{"description":"Array of tasks in this column. Tasks are sorted by priority (descending) then shortNumber (ascending), with pipeline tasks appearing first.","type":"array","items":{"$ref":"#/components/schemas/StackTaskDetailsDto"}}},"required":["type","tasks"]},"StackResponseDto":{"type":"object","properties":{"columns":{"description":"Array of stack columns representing different workflow stages. Always includes all five column types (toDo, giveFeedback, getFeedback, done, backlog), even if some columns are empty. Columns appear in this order. **Task filtering**: Pipeline tasks (regardless of assignment) appear in toDo and done columns. Non-pipeline tasks filtered by Assigned field (assignedToId) appear in giveFeedback, getFeedback, and backlog columns. Tasks are sorted by priority and pipeline order.","type":"array","items":{"$ref":"#/components/schemas/StackColumnDto"}}},"required":["columns"]},"PipelineResponseDto":{"type":"object","properties":{"users":{"type":"object","description":"Object containing pipeline data for each user, keyed by userId. Each entry contains employee information, days array (work day data), items array (planned tasks), and reality array (time logs if enabled).","additionalProperties":{"type":"object","$ref":"#/components/schemas/PipelineUserRowDto"},"example":{"user-id-1":{"employeeId":"employee-id-1","userId":"user-id-1","name":"John Doe","days":[],"items":[],"reality":[]}}},"ready":{"type":"boolean","description":"Whether the pipeline week is marked as active/ready. When true, the planned tasks are visible in employee stacks. When false, the week is still in draft/planning mode.","example":true},"year":{"type":"number","description":"Year of the pipeline period start date (ISO week year)","example":2025},"week":{"type":"number","description":"ISO week number of the pipeline period start date","example":43}},"required":["users","ready","year","week"]},"ActivatePipelineDto":{"type":"object","properties":{"dateStart":{"type":"string","description":"Start date of the pipeline week in ISO format (YYYY-MM-DD). The week is determined from this date using ISO week numbering.","example":"2025-11-03"},"dateEnd":{"type":"string","description":"End date of the pipeline week in ISO format (YYYY-MM-DD). Should match the end of the week period (typically 1-2 weeks from dateStart based on workspace settings).","example":"2025-11-16"}},"required":["dateStart","dateEnd"]},"CreatePipelineItemDto":{"type":"object","properties":{"taskId":{"type":"string","description":"Unique identifier of the task to plan in the pipeline. The task must exist and the user must have access to view it.","example":"123e4567-e89b-12d3-a456-426614174000"},"userId":{"type":"string","description":"Unique identifier of the user/team member to assign this task to. Permission restrictions apply: users with Pipeline.canPlanOwnTeams can only assign to themselves or team members, while users with Pipeline.canPlanAll can assign to any user.","example":"123e4567-e89b-12d3-a456-426614174001"},"dateStart":{"type":"string","description":"Start date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeStart to specify the actual start time relative to work day start. Example: \"2025-11-04T00:00:00Z\" (the time part is ignored).","example":"2025-11-04T00:00:00Z"},"dateEnd":{"type":"string","description":"End date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeEnd to specify the actual end time relative to work day start. Maximum 2 weeks from dateStart. Example: \"2025-11-04T00:00:00Z\" (the time part is ignored).","example":"2025-11-04T00:00:00Z"},"timeStart":{"type":"number","description":"**CRITICAL**: Start time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). \n\n**Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)\n**Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.\n\n**Examples** (assuming 8-hour work day starting at 09:00):\n- `timeStart: 0` = 09:00 (start of work day)\n- `timeStart: 60` = 10:00 (1 hour after work day start)\n- `timeStart: 240` = 13:00 (4 hours after work day start = midday)\n- `timeStart: 480` = 17:00 (8 hours = end of work day)\n\n**Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):\nTo schedule multiple tasks sequentially throughout the day, chain them together:\n- Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)\n- Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)\n- Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)\n- Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)\n- Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)\n\n**Calculation**: Actual time = workDayStartTime + (timeStart minutes)\n**Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeStart=0 means 09:00, not 00:00.","example":0,"minimum":0},"timeEnd":{"type":"number","description":"**CRITICAL**: End time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). \n\n**Range**: Must be greater than timeStart, up to workDayLength (typically 0-480 for an 8-hour day)\n**Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.\n\n**Examples** (assuming 8-hour work day starting at 09:00):\n- `timeEnd: 240` = 13:00 (4 hours after work day start = midday)\n- `timeEnd: 330` = 14:30 (5.5 hours after work day start)\n- `timeEnd: 480` = 17:00 (8 hours = end of work day)\n\n**Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):\nTo schedule multiple tasks sequentially throughout the day, chain them together:\n- Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)\n- Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)\n- Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)\n- Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)\n- Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)\n\n**Calculation**: Actual time = workDayStartTime + (timeEnd minutes)\n**Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeEnd=480 means 17:00, not 08:00. Must be greater than timeStart.","example":240,"minimum":0}},"required":["taskId","userId","dateStart","dateEnd","timeStart","timeEnd"]},"PipelineItemResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the created or updated pipeline item","example":"123e4567-e89b-12d3-a456-426614174002"},"taskId":{"type":"string","description":"Unique identifier of the task that was planned","example":"123e4567-e89b-12d3-a456-426614174000"},"userId":{"type":"string","description":"Unique identifier of the user/team member the task is assigned to","example":"123e4567-e89b-12d3-a456-426614174001"},"dateStart":{"type":"string","description":"Start date and time in ISO format (RESPONSE VALUE - this is different from input). **Note**: In the request, the time component of dateStart is IGNORED. In the response, the time component IS meaningful - it represents the calculated absolute time based on work day start + timeStart minutes. The date part (YYYY-MM-DD) represents the calendar day. When reading this response value, extract the time relative to work day start, not midnight. Example: If work day starts at 09:00 and timeStart was 30, this response will show 09:30 on the specified date.","example":"2025-11-04T09:30:00Z"},"dateEnd":{"type":"string","description":"End date and time in ISO format (RESPONSE VALUE - this is different from input). **Note**: In the request, the time component of dateEnd is IGNORED. In the response, the time component IS meaningful - it represents the calculated absolute time based on work day start + timeEnd minutes. The date part (YYYY-MM-DD) represents the calendar day. When reading this response value, extract the time relative to work day start, not midnight. Example: If work day starts at 09:00 and timeEnd was 330, this response will show 14:30 (5.5 hours from 09:00) on the specified date.","example":"2025-11-04T14:30:00Z"}},"required":["id","taskId","userId","dateStart","dateEnd"]},"UpdatePipelineItemDto":{"type":"object","properties":{"taskId":{"type":"string","description":"Unique identifier of the task to plan. Optional - if not provided, the existing taskId is preserved. The task must exist and the user must have access to view it.","example":"123e4567-e89b-12d3-a456-426614174000"},"userId":{"type":"string","description":"Unique identifier of the user/team member to assign this task to. Optional - if not provided, the existing userId is preserved. Permission restrictions apply: users with Pipeline.canPlanOwnTeams can only assign to themselves or team members, while users with Pipeline.canPlanAll can assign to any user.","example":"123e4567-e89b-12d3-a456-426614174001"},"dateStart":{"type":"string","description":"Start date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeStart to specify the actual start time relative to work day start. Optional - if not provided, the existing dateStart is preserved. Example: \"2025-11-04T00:00:00Z\" (the time part is ignored).","example":"2025-11-04T00:00:00Z"},"dateEnd":{"type":"string","description":"End date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeEnd to specify the actual end time relative to work day start. Maximum 2 weeks from dateStart. Optional - if not provided, the existing dateEnd is preserved. Example: \"2025-11-04T00:00:00Z\" (the time part is ignored).","example":"2025-11-04T00:00:00Z"},"timeStart":{"type":"number","description":"**CRITICAL**: Start time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). Optional - if not provided, the existing timeStart is preserved.\n\n**Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)\n**Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.\n\n**Examples** (assuming 8-hour work day starting at 09:00):\n- `timeStart: 0` = 09:00 (start of work day)\n- `timeStart: 60` = 10:00 (1 hour after work day start)\n- `timeStart: 240` = 13:00 (4 hours after work day start = midday)\n- `timeStart: 480` = 17:00 (8 hours = end of work day)\n\n**Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):\nTo schedule multiple tasks sequentially throughout the day, chain them together:\n- Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)\n- Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)\n- Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)\n- Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)\n- Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)\n\n**Calculation**: Actual time = workDayStartTime + (timeStart minutes)\n**Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeStart=0 means 09:00, not 00:00.","example":0,"minimum":0},"timeEnd":{"type":"number","description":"**CRITICAL**: End time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). Optional - if not provided, the existing timeEnd is preserved.\n\n**Range**: Must be greater than timeStart, up to workDayLength (typically 0-480 for an 8-hour day)\n**Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.\n\n**Examples** (assuming 8-hour work day starting at 09:00):\n- `timeEnd: 240` = 13:00 (4 hours after work day start = midday)\n- `timeEnd: 330` = 14:30 (5.5 hours after work day start)\n- `timeEnd: 480` = 17:00 (8 hours = end of work day)\n\n**Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):\nTo schedule multiple tasks sequentially throughout the day, chain them together:\n- Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)\n- Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)\n- Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)\n- Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)\n- Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)\n\n**Calculation**: Actual time = workDayStartTime + (timeEnd minutes)\n**Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeEnd=480 means 17:00, not 08:00. Must be greater than timeStart.","example":240,"minimum":0}}},"TranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"Language code (e.g., en, es, fr)"},"name":{"type":"string","description":"Translated name"},"description":{"type":"object","description":"Translated description"}},"required":["language","name"]},"TaskTypeDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the task type"},"name":{"type":"string","description":"Name of the task type (e.g., Bug, Feature, Support Request). Used for identification in the UI."},"icon":{"type":"string","description":"Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:star:`). Used for visual representation in task lists and views."},"type":{"type":"string","enum":["Feature","Bug"],"description":"Base type category: Feature (value-adding work) or Bug (bug fix, typically not billable)"},"sort":{"type":"number","description":"Sort order for displaying task types in lists. Lower numbers appear first."},"statuses":{"description":"Array of task status UUIDs that are valid for this task type. Defines the workflow states available for tasks of this type.","type":"array","items":{"type":"string"}},"customFields":{"description":"Array of custom field UUIDs associated with this task type. These custom fields appear when creating or editing tasks of this type.","type":"array","items":{"type":"string"}},"requiredFields":{"description":"Array of field names that are required when creating tasks of this type. Ensures essential information is always captured.","type":"array","items":{"type":"string"}},"translations":{"description":"Multilingual translations for the task type name. Supports displaying the type name in different languages based on user preferences.","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}},"deleted":{"type":"boolean","description":"Whether this task type has been soft-deleted. Deleted types are typically hidden from active use but retained for historical data."}},"required":["id","name","icon","type","sort","statuses","customFields","requiredFields","translations","deleted"]},"TaskStatusDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the task status"},"name":{"type":"string","description":"Name of the task status (e.g., New, In Progress, Feedback, Resolved, Closed, Backlog). Used for identification in the UI."},"icon":{"type":"string","description":"Icon identifier in the format `:icon_name:` (e.g., `:hourglass_flowing_sand:`, `:check_mark:`, `:x:`). Used for visual representation in task lists, Kanban boards, and status selectors."},"type":{"type":"string","enum":["New","InProgress","Feedback","Backlog","Resolved","Closed"],"description":"Base status type: New, InProgress, Feedback, Backlog, Resolved, or Closed. Defines the semantic meaning and behavior of the status."},"sort":{"type":"number","description":"Sort order for displaying task statuses in lists and dropdowns. Lower numbers appear first."},"translations":{"description":"Multilingual translations for the task status name. Supports displaying the status name in different languages based on user preferences.","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}},"deleted":{"type":"boolean","description":"Whether this task status has been soft-deleted. Deleted statuses are typically hidden from active use but retained for historical data."}},"required":["id","name","icon","type","sort","translations","deleted"]},"SelectOptionDto":{"type":"object","properties":{"value":{"type":"string","description":"Option value"},"translations":{"description":"Option translations","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}}},"required":["value","translations"]},"TaskCustomFieldDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the custom field"},"name":{"type":"string","description":"Name of the custom field (e.g., Risk Level, Affected Component, SLA Level). Used as the field label in forms."},"description":{"type":"object","description":"Optional description explaining what this custom field is used for and how to fill it out"},"type":{"type":"string","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"description":"Type of the custom field: Text (short entries), TextArea (longer entries), Number (numeric values), Date (date selection), Checkbox (Yes/No), or Select (dropdown with predefined options)"},"sort":{"type":"number","description":"Sort order for displaying custom fields in forms. Lower numbers appear first."},"entity":{"type":"string","enum":["Task","Project","Organization","Object"],"description":"Entity this field belongs to. For task custom fields, this is always Task."},"translations":{"description":"Multilingual translations for the custom field name and description. Supports displaying field labels in different languages based on user preferences.","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}},"selectOptions":{"description":"Array of select options for dropdown (Select) type fields. Each option has a value and multilingual translations. Only populated for Select type fields.","type":"array","items":{"$ref":"#/components/schemas/SelectOptionDto"}}},"required":["id","name","type","sort","entity","translations","selectOptions"]},"TaskCustomFieldValueDto":{"type":"object","properties":{"fieldId":{"type":"string","description":"UUID of the custom field. Must be a custom field that is associated with the task type being used.","example":"550e8400-e29b-41d4-a716-446655440000"},"value":{"type":"string","description":"Value for the custom field. The format depends on the field type: Text/TextArea (string), Number (numeric string), Date (ISO 8601 date string), Checkbox (true/false string), Select (one of the predefined option values). Validation is performed based on the field type configuration.","example":"Custom value"}},"required":["fieldId","value"]},"CreateTaskDto":{"type":"object","properties":{"title":{"type":"string","description":"Task title or name. This is the main identifier shown in task lists and views.","example":"Implement new feature"},"projectId":{"type":"string","description":"UUID of the project this task belongs to. The project must exist and the user must have access to it.","example":"550e8400-e29b-41d4-a716-446655440000"},"typeId":{"type":"string","description":"UUID of the task type. The type defines the workflow, available statuses, and custom fields. Must be one of the task types allowed for this project (configured in the project settings). Use the GET /projects/:id endpoint to see which task types are available for the project.","example":"660e8400-e29b-41d4-a716-446655440001"},"statusId":{"type":"string","description":"UUID of the initial task status. The status must be valid for the selected task type. Common statuses include New, In Progress, Feedback, Resolved, Closed, and Backlog.","example":"770e8400-e29b-41d4-a716-446655440002"},"priority":{"type":"string","description":"Task priority level indicating urgency. Options: Low, Normal, High. Used for prioritization and filtering.","enum":["Low","Normal","High"],"example":"Normal"},"description":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<p>Task description in HTML</p>"},"icon":{"type":"object","description":"Task icon identifier. Must be in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:memo:`, `:white_check_mark:`). Used for visual identification in task lists and views. Leave null to use the default icon from the task type.","example":":rocket:"},"assignedToId":{"type":"object","description":"UUID of the user assigned to work on this task. The assigned user receives notifications about task updates. Leave null if no one is assigned yet.","example":"880e8400-e29b-41d4-a716-446655440003","nullable":true},"accountableId":{"type":"object","description":"UUID of the user who is accountable for this task. The accountable person is responsible for ensuring the task is completed. This is separate from the assigned user. Leave null if no one is accountable yet.","example":"990e8400-e29b-41d4-a716-446655440004","nullable":true},"summary":{"type":"string","description":"Brief summary or short description of the task. This is a concise overview that appears in task cards and lists. Can be auto-generated by AI or manually entered.","example":"Brief summary of the task"},"estimatedTime":{"type":"number","description":"Estimated time to complete the task in hours (decimal allowed). Used for planning and capacity management. Example: 8 for 8 hours, 2.5 for 2.5 hours.","example":2.5},"deadline":{"type":"string","description":"Task deadline or due date in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ). Used for tracking deadlines and generating deadline warnings.","example":"2024-12-31T23:59:59Z"},"guestAccess":{"type":"object","description":"Whether organization members (guest users) can access this task. When true, guest users can view and interact with the task. When false, only workspace members can access it. Leave null to use the project default.","example":false,"nullable":true,"default":false},"customFields":{"description":"Array of custom field values. Each value must correspond to a custom field that is associated with the selected task type. The field value format depends on the field type (text, number, date, checkbox, select).","type":"array","items":{"$ref":"#/components/schemas/TaskCustomFieldValueDto"}},"tags":{"description":"Array of tag UUIDs to categorize this task. Tags enable cross-project organization and filtering. Tags can be automatically inherited from project components.","example":["aa0e8400-e29b-41d4-a716-446655440005"],"type":"array","items":{"type":"string"}},"subTasksIds":{"description":"Array of existing task UUIDs to add as subtasks. Subtasks allow breaking down complex tasks into smaller units. Each subtask must exist and be accessible. The parent-child relationship is established immediately.","example":["bb0e8400-e29b-41d4-a716-446655440006"],"type":"array","items":{"type":"string"}},"linkedObjectIds":{"description":"Object instance UUIDs to link to the new task via ObjectsOnTask. Requires the same permission as linking objects on an existing task (`objects.canEdit`). Invalid or inaccessible object IDs result in an error and the task is not created.","type":"array","items":{"type":"string"}},"products":{"description":"Array of product settings to associate products with this task. Used for billing and quotation purposes. Each product setting includes the product ID, quantity, variant, and options.","type":"array","items":{"$ref":"#/components/schemas/TaskProductSettingsDto"}},"componentItemEntityUniqueId":{"type":"object","description":"Component item entity unique ID if this task is related to a project component item. Used for linking tasks to specific components in project structures.","example":"unique-component-id"},"isChangeRequest":{"type":"boolean","description":"Whether this task is a change request. Change requests are typically billed separately and tracked differently in Single projects. Used for scope change management.","example":false},"enableSupportContingent":{"type":"boolean","description":"Enable support contingent for this task. This flag marks a task as eligible for support contingent billing. Only available for Support projects.","example":false}},"required":["title","projectId","typeId","statusId","priority","description"]},"TaskDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Task ID (UUID)"},"title":{"type":"string","description":"Task title"},"titleIsGenerating":{"type":"boolean","description":"Whether title is being generated by AI"},"icon":{"type":"object","description":"Task icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:memo:`). Can be null if no custom icon is set."},"iconIsGenerating":{"type":"boolean","description":"Whether icon is being generated by AI"},"summaryIsGenerating":{"type":"boolean","description":"Whether summary is being generated by AI"},"createdAt":{"format":"date-time","type":"string","description":"Task creation timestamp"},"deadline":{"format":"date-time","type":"string","description":"Task deadline"},"priority":{"type":"object","description":"Task priority (TaskPriority enum)"},"projectId":{"type":"string","description":"Project ID"},"statusId":{"type":"string","description":"Task status ID"},"userId":{"type":"string","description":"Creator user ID"},"assignedToId":{"type":"string","description":"Assigned user ID"},"accountableId":{"type":"string","description":"Accountable user ID"},"summary":{"type":"string","description":"Task summary"},"typeId":{"type":"string","description":"Task type ID"},"shortNumber":{"type":"number","description":"Task short number (workspace-scoped)"},"estimatedTime":{"type":"number","description":"Estimated time in hours"},"description":{"type":"string","description":"Task description in HTML. May include embedded assets as elements such as `<div data-type=\"appImage\" fileId=\"…\" filename=\"…\">` (images), `appFile`, or `appVideo`—binary content is not embedded; use GET `{appOrigin}/api/files/public/{fileId}` on the Leadtime web app host to retrieve the file for viewing or vision models."},"guestAccess":{"type":"boolean","description":"Guest access enabled for organization members"},"project":{"type":"object","description":"Project information"},"accountable":{"type":"object","description":"Accountable user details"},"assignedTo":{"type":"object","description":"Assigned user details"},"customFields":{"type":"object","description":"Custom fields values"},"history":{"description":"Task history entries","type":"array","items":{"type":"array"}},"comments":{"description":"Task comments; each `body` is HTML and may contain the same embedded `appImage` / `appFile` / `appVideo` divs with `fileId` as the task description. Fetch binaries via `/api/files/public/{fileId}` on the app origin when needed.","type":"array","items":{"type":"array"}},"quotations":{"description":"Express quotations","type":"array","items":{"type":"array"}},"spentTime":{"type":"number","description":"Total spent time in minutes"},"childSpentTime":{"type":"number","description":"Child tasks spent time in minutes"},"participantsCount":{"type":"number","description":"Number of participants"},"participantsList":{"description":"List of participant user IDs","type":"array","items":{"type":"array"}},"isParticipant":{"type":"boolean","description":"Whether current user is a participant"},"notificationSettings":{"type":"object","description":"Notification settings"},"tags":{"description":"Task tags","type":"array","items":{"type":"array"}},"parentTaskId":{"type":"object","description":"Parent task ID for subtasks"},"subTasksIds":{"description":"Child task IDs","type":"array","items":{"type":"array"}},"skipFromBilling":{"type":"boolean","description":"Skip from billing"},"billed":{"type":"boolean","description":"Whether task is billed"},"products":{"description":"Task products","type":"array","items":{"type":"array"}},"componentItemId":{"type":"object","description":"Component item ID"},"isChangeRequest":{"type":"boolean","description":"Whether task is a change request"},"emailDetails":{"type":"object","description":"Email details"},"emailSendError":{"type":"string","description":"Email send error message"},"emailSendErrorCode":{"type":"string","description":"Email send error code"},"emailSendFailedAt":{"format":"date-time","type":"string","description":"Email send failed timestamp"},"emailSendFailedTo":{"description":"Email send failed recipients","type":"array","items":{"type":"array"}},"enableSupportContingent":{"type":"boolean","description":"Enable support contingent"}},"required":["id","title","titleIsGenerating","iconIsGenerating","summaryIsGenerating","createdAt","priority","projectId","statusId","userId","typeId","shortNumber","description","guestAccess","project","customFields","history","comments","quotations","spentTime","childSpentTime","participantsCount","participantsList","isParticipant","notificationSettings","tags","subTasksIds","billed","products","isChangeRequest","enableSupportContingent"]},"PatchTaskDto":{"type":"object","properties":{"title":{"type":"string","description":"Task title or name. Updates the main identifier shown in task lists and views."},"icon":{"type":"object","description":"Task icon identifier. Must be in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:memo:`, `:white_check_mark:`). Used for visual identification. Set to null to remove the custom icon and use the task type default."},"description":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n"},"summary":{"type":"string","description":"Brief summary or short description of the task. Appears in task cards and lists. Can be auto-generated by AI or manually entered."},"priority":{"type":"string","enum":["Low","Normal","High"],"description":"Task priority level: Low, Normal, or High. Used for prioritization and filtering."},"statusId":{"type":"string","description":"UUID of the task status to change to. The status must be valid for the current task type. Changing status updates the task workflow state."},"typeId":{"type":"string","description":"UUID of the task type to change to. Changing the type may automatically update the status if the current status is not valid for the new type. Also updates available custom fields."},"projectId":{"type":"string","description":"UUID of the project to move the task to. Changing the project cascades to all subtasks and time logs, moving them to the new project as well. The user must have access to the new project."},"assignedToId":{"type":"object","description":"UUID of the user to assign the task to. The assigned user receives notifications about task updates. Set to null to unassign the task (remove the assignment)."},"accountableId":{"type":"object","description":"UUID of the user who is accountable for the task. The accountable person is responsible for ensuring completion. Set to null to remove accountability."},"estimatedTime":{"type":"number","description":"Estimated time to complete the task in hours (decimal allowed). Example: 8 for 8 hours, 2.5 for 2.5 hours.","example":2.5},"deadline":{"type":"string","description":"Task deadline or due date in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ). Used for tracking deadlines and generating deadline warnings. Set to null to remove the deadline."},"guestAccess":{"type":"boolean","description":"Whether organization members (guest users) can access this task. When true, guest users can view and interact with the task. When false, only workspace members can access it.","default":false},"tags":{"description":"Array of tag UUIDs to categorize this task. Replaces all existing tags. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.","type":"array","items":{"type":"string"}},"customFields":{"type":"array","description":"Array of custom field values to update. Each value must correspond to a custom field associated with the task type. The field value format depends on the field type. Replaces all existing custom field values. Use an empty array to clear all custom fields.","items":{"type":"object","properties":{"fieldId":{"type":"string","format":"uuid","description":"Custom field ID (UUID)"},"value":{"type":"string","description":"Field value (format depends on field type)"}}}},"isChangeRequest":{"type":"boolean","description":"Whether this task is a change request. Change requests are typically billed separately and tracked differently in Single projects. Used for scope change management."},"subTasksIds":{"description":"Array of task UUIDs to set as subtasks. Replaces all existing subtasks. Each subtask must exist and be accessible. Use an empty array to remove all subtasks.","type":"array","items":{"type":"string"}},"enableSupportContingent":{"type":"boolean","description":"Enable support contingent for this task"}}},"AddTaskCommentDto":{"type":"object","properties":{"comment":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<p>This is a comment in HTML</p>"},"assignedToId":{"type":"object","description":"Optional UUID of the user to assign the task to. If provided, the task assignee is updated when the comment is created. Leave null to keep the current assignee.","example":"550e8400-e29b-41d4-a716-446655440000"},"statusId":{"type":"object","description":"Optional UUID of the task status to update the task to. If provided, the task status is changed when the comment is created. The status must be valid for the task type.","example":"660e8400-e29b-41d4-a716-446655440000"},"timeBooking":{"type":"object","description":"Optional time booking data to log working hours on the task. If provided, time is logged when the comment is created. Includes hours (number) and activityId (UUID of the work activity type).","example":{"hours":2,"activityId":"770e8400-e29b-41d4-a716-446655440000"}},"guestAccess":{"type":"object","description":"Whether this comment should be visible to organization members (guest users). When true, the comment is public and visible to guests. When false, the comment is internal and only visible to workspace members. Leave null to use the task default.","default":false},"helpdeskEmailId":{"type":"string","description":"UUID of the helpdesk email this comment is related to. Used for linking comments to incoming helpdesk emails in the helpdesk integration.","example":"880e8400-e29b-41d4-a716-446655440000"},"sendHelpdeskTo":{"description":"Array of email addresses to send the comment to via helpdesk. Used for sending task comments as emails to external recipients (e.g., customers). Each email address must be valid.","example":["user@example.com"],"type":"array","items":{"type":"array"}}},"required":["comment"]},"TaskCommentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Comment unique identifier (UUID)"},"userId":{"type":"string","description":"User ID of the comment author who created this comment"},"createdAt":{"format":"date-time","type":"string","description":"Timestamp when the comment was originally created (ISO 8601 format)"},"lastUpdated":{"format":"date-time","type":"string","description":"Timestamp when the comment was last modified (ISO 8601 format). Updated when the comment body is edited."},"body":{"type":"string","description":"Comment body content in HTML format. Converted from the internal editor format for API consumption. May include formatting, links, mentions, and other rich text elements."},"guestAccess":{"type":"boolean","description":"Whether guest access (public visibility) is enabled for this comment. When true, organization members (guest users) can see this comment."},"emailDetails":{"type":"object","description":"Email details if this comment was sent via email (helpdesk integration)"},"emailSendError":{"type":"string","description":"Error message if email sending failed for this comment"},"emailSendErrorCode":{"type":"string","description":"Error code categorizing the email send failure"},"emailSendFailedAt":{"format":"date-time","type":"string","description":"Timestamp when email sending failed (ISO 8601 format)"},"emailSendFailedTo":{"description":"Array of email addresses where email sending failed","type":"array","items":{"type":"array"}}},"required":["id","userId","createdAt","lastUpdated","body","guestAccess"]},"UpdateTaskCommentDto":{"type":"object","properties":{"comment":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<p>Updated comment with <strong>formatting</strong></p>"}},"required":["comment"]},"AddSubtaskDto":{"type":"object","properties":{"subtaskId":{"type":"string","description":"UUID of an existing task to add as a subtask to the parent task. The subtask must exist in the workspace and be accessible to the user. The subtask will be linked to the parent task specified in the URL path.","example":"550e8400-e29b-41d4-a716-446655440000"}},"required":["subtaskId"]},"GetProjectBillingSettingsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the project. Format: UUID v4.","example":"123e4567-e89b-12d3-a456-426614174000"},"allowMixedBilling":{"type":"boolean","description":"Controls invoice structure: true = combined invoices (support and subscriptions on one invoice), false = split invoices (support invoices generated separately). Combined invoices bill support efforts by booked hours and subscriptions together. Split invoices generate separate invoices for each billing type.","example":true},"bugsAreBillable":{"type":"boolean","description":"Determines whether time spent on tasks with \"Bug\" activity type can be billed to the customer. When true, bug tasks are included in billable hours. When false, bug tasks are excluded from billing calculations.","example":true},"includeComponentsInBilling":{"type":"boolean","description":"When true, component hours and amounts are included in estimates and invoices. When false, component totals are excluded from billing calculations.","example":true},"invoiceContactId":{"type":"object","description":"Default invoice recipient contact person from the customer organization. This is the OrganizationMember ID who will receive invoices. This person is auto-filled when creating invoices but can be changed during invoice creation. Set to null if no default recipient is configured.","example":"123e4567-e89b-12d3-a456-426614174001","nullable":true},"billingProjectSnapshotId":{"type":"object","description":"Project version snapshot ID used as the basis for main billing. This version includes components, manual items, and products as agreed with the customer. Only applicable for Single project type. The selected version determines what items and prices are included when generating invoices for the project.","example":"123e4567-e89b-12d3-a456-426614174002","nullable":true},"productsBillingProjectSnapshotId":{"type":"object","description":"Project version snapshot ID used for billing subscriptions independently of the main project scope. This allows products with recurring payments to be billed separately using a different version snapshot. Only applicable for Single project type. If not set, the main billing version (billingProjectSnapshotId) is used automatically for subscriptions.","example":"123e4567-e89b-12d3-a456-426614174003","nullable":true}},"required":["id","allowMixedBilling","bugsAreBillable","includeComponentsInBilling","invoiceContactId","billingProjectSnapshotId","productsBillingProjectSnapshotId"]},"PatchProjectBillingSettingsDto":{"type":"object","properties":{"allowMixedBilling":{"type":"boolean","description":"Controls invoice structure: true = combined invoices (support and subscriptions on one invoice), false = split invoices (support invoices generated separately). Optional field - if not provided, current value remains unchanged.","example":false},"bugsAreBillable":{"type":"boolean","description":"Determines whether time spent on tasks with \"Bug\" activity type can be billed to the customer. Optional field - if not provided, current value remains unchanged.","example":false},"includeComponentsInBilling":{"type":"boolean","description":"When true, component hours and amounts are included in estimates and invoices. When false, component totals are excluded. Optional field - if not provided, current value remains unchanged.","example":false},"invoiceContactId":{"type":"object","description":"Default invoice recipient contact person from the customer organization (OrganizationMember ID). This person is auto-filled when creating invoices but can be changed during invoice creation. Set to null to remove the default recipient. Optional field - if not provided, current value remains unchanged.","example":"123e4567-e89b-12d3-a456-426614174001","nullable":true},"billingProjectSnapshotId":{"type":"object","description":"Project version snapshot ID used as the basis for main billing. Only applicable for Single project type. The selected version determines what items and prices are included when generating invoices. Optional field - if not provided, current value remains unchanged.","example":"123e4567-e89b-12d3-a456-426614174002","nullable":true},"productsBillingProjectSnapshotId":{"type":"object","description":"Project version snapshot ID used for billing subscriptions independently of the main project scope. Only applicable for Single project type. If not set, the main billing version is used automatically. Optional field - if not provided, current value remains unchanged.","example":"123e4567-e89b-12d3-a456-426614174003","nullable":true}}},"CreateProjectComponentDto":{"type":"object","properties":{"name":{"type":"string","description":"Component name","example":"Authentication Module"},"description":{"type":"string","description":"Component description in HTML or Markdown format","example":"<p>User authentication and authorization module</p>"},"icon":{"type":"object","description":"Icon identifier for the component","example":"icon-lock"},"tags":{"description":"Array of tag IDs to associate with the component","example":["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML or Markdown format","example":"<p>This is an internal development note</p>"}},"required":["name","description"]},"CreateProjectComponentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the created component","example":"f1a2b3c4-d5e6-7890-abcd-ef1234567890"}},"required":["id"]},"ImportComponentsFromLibraryDto":{"type":"object","properties":{"componentIds":{"description":"Array of library component IDs to import","example":["e57dc37b-7693-4d06-b49c-17084b773aff","a1b2c3d4-e5f6-7890-abcd-ef1234567890"],"type":"array","items":{"type":"string"}}},"required":["componentIds"]},"ImportComponentsFromLibraryResponseDto":{"type":"object","properties":{"ids":{"description":"Array of imported component IDs","example":["f1a2b3c4-d5e6-7890-abcd-ef1234567890"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"CreateTodoDto":{"type":"object","properties":{"title":{"type":"string","description":"Todo title. Todos are checklist items used to track actionable tasks within component items. They are perfect for recurring processes, quality control checkpoints, and project preparation steps.","example":"Implement authentication with 2FA"},"description":{"type":"string","description":"Todo description in HTML or Markdown format. Todos are checklist items that can be checked off individually and support comments.","example":"<p>Add user login with two-factor authentication</p>"}},"required":["title","description"]},"UpdateTodoDto":{"type":"object","properties":{"title":{"type":"string","description":"Todo title","example":"Implement authentication with 2FA"},"description":{"type":"string","description":"Todo description in HTML or Markdown format","example":"<p>Add user login with two-factor authentication</p>"}}},"CreateQuestionOptionDto":{"type":"object","properties":{"title":{"type":"string","description":"Option title","example":"Option A"},"extraHours":{"type":"number","description":"Extra hours for this option","example":2.5}},"required":["title"]},"CreateComponentItemConditionDto":{"type":"object","properties":{"type":{"type":"string","description":"Condition type determines what must be true for the condition to be met. Available types depend on the target element: QuestionAnswered, QuestionNotAnswered, QuestionAnswerContains, QuestionAnswerDoesNotContain, QuestionAnswerEqual, QuestionAnswerDoesNotEqual (for questions), TodoIsDone, TodoIsNotDone (for todos), TestCasePassed, TestCaseFailed (for test cases).","enum":["QuestionAnswered","QuestionNotAnswered","QuestionAnswerContains","QuestionAnswerDoesNotContain","QuestionAnswerEqual","QuestionAnswerDoesNotEqual","TodoIsDone","TodoIsNotDone","TestCasePassed","TestCaseFailed"],"example":"QuestionAnswered"},"targetValue":{"type":"string","description":"Target value to compare against","example":"Yes"},"targetQuestionId":{"type":"string","description":"Target question ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetTodoId":{"type":"string","description":"Target todo ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetTestCaseId":{"type":"string","description":"Target test case ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"containsType":{"type":"string","description":"Contains type for condition","enum":["Some","All"],"example":"Some"}},"required":["type"]},"CreateProjectQuestionDto":{"type":"object","properties":{"title":{"type":"string","description":"Question title/heading. Questions capture customer-specific requirements or project details. They make work packages flexible and reusable by allowing later individualization in projects.","example":"What is the expected performance?"},"question":{"type":"string","description":"The question text","example":"Please specify the expected response time"},"description":{"type":"string","description":"Question description (accepts HTML or Markdown, returns HTML)","example":"<p>This question helps us understand performance requirements.</p>"},"type":{"type":"string","description":"Question type determines what kind of answer is expected and how it is collected. Each type is suited for different data collection needs:\n- **ShortText**: Single-line text input for names, titles, keywords, or short answers\n- **Editor**: Formatted multi-line text for descriptions, detailed explanations, or free text answers\n- **Checkbox**: Yes/No selection or multiple checkbox options. Answers can automatically impact effort calculation and pricing if options have extraHours configured\n- **Radio**: Single choice from multiple predefined options. Answers can automatically impact effort calculation and pricing if options have extraHours configured\n- **Files**: Upload files such as logos, documents, graphics, or reference materials\n- **Datepicker**: Select a date for deadlines, delivery dates, or scheduling\n- **Multiplier**: Dynamic calculation based on quantity. The system calculates: Answer (quantity) × Duration per element = Total additional effort. Perfect for questions like \"How many pages?\" where effort scales with quantity\n- **Person**: Select one or more people from the project team or clients","enum":["ShortText","Editor","Checkbox","Radio","Files","Datepicker","Multiplier","Person"],"example":"ShortText"},"options":{"description":"Array of answer options for Checkbox or Radio question types. Required for Checkbox/Radio types and must have at least 1 item. Each option can have an optional extraHours value that affects effort calculation when selected. For Checkbox questions, multiple options can be selected. For Radio questions, only one option can be selected.","type":"array","items":{"$ref":"#/components/schemas/CreateQuestionOptionDto"}},"multiplierValue":{"type":"number","description":"Multiplier value (duration per element) for Multiplier question type. This represents the time needed for a single element (e.g., per page, per product, per user). When the question is answered with a quantity, the system calculates: Answer (quantity) × multiplierValue = Total additional effort in hours. For example, if multiplierValue is 2.5 and the answer is 10 pages, the total additional effort is 25 hours.","example":1.5},"conditions":{"description":"Array of conditions that control question visibility/behavior","type":"array","items":{"$ref":"#/components/schemas/CreateComponentItemConditionDto"}}},"required":["title","question","description","type"]},"UpdateQuestionOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Option ID - if provided, updates existing option; if omitted, creates new option","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"title":{"type":"string","description":"Option title","example":"Option A"},"extraHours":{"type":"number","description":"Extra hours for this option","example":2.5}},"required":["title"]},"UpdateComponentItemConditionDto":{"type":"object","properties":{"id":{"type":"string","description":"Condition ID - if provided, updates existing condition; if omitted, creates new condition","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"type":{"type":"string","description":"Condition type","enum":["QuestionAnswered","QuestionNotAnswered","QuestionAnswerContains","QuestionAnswerDoesNotContain","QuestionAnswerEqual","QuestionAnswerDoesNotEqual","TodoIsDone","TodoIsNotDone","TestCasePassed","TestCaseFailed"],"example":"QuestionAnswered"},"targetQuestionId":{"type":"string","description":"Target question ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetTodoId":{"type":"string","description":"Target todo ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetTestCaseId":{"type":"string","description":"Target test case ID","example":"e57dc37b-7693-4d06-b49c-17084b773aff"},"targetValue":{"type":"string","description":"Target value to compare against","example":"Yes"},"containsType":{"type":"string","description":"Contains type for condition","enum":["Some","All"],"example":"Some"}},"required":["type"]},"UpdateProjectQuestionDto":{"type":"object","properties":{"title":{"type":"string","description":"Question title/heading","example":"What is the expected performance?"},"question":{"type":"string","description":"The question text","example":"Please specify the expected response time"},"description":{"type":"string","description":"Question description (accepts HTML or Markdown, returns HTML)","example":"<p>This question helps us understand performance requirements.</p>"},"type":{"type":"string","description":"Question type","enum":["ShortText","Editor","Checkbox","Radio","Files","Datepicker","Multiplier","Person"],"example":"ShortText"},"options":{"description":"Array of answer options (creates/updates/deletes based on presence of id). Required if type is updated to Checkbox/Radio.","type":"array","items":{"$ref":"#/components/schemas/UpdateQuestionOptionDto"}},"multiplierValue":{"type":"number","description":"Multiplier value for calculations","example":1.5},"conditions":{"description":"Array of conditions that control question visibility/behavior (creates/updates/deletes based on presence of id)","type":"array","items":{"$ref":"#/components/schemas/UpdateComponentItemConditionDto"}}}},"SortProjectQuestionsDto":{"type":"object","properties":{"newSortOrder":{"description":"Array of question IDs in new order. All questions for the item must be included.","example":["f68ed48c-8704-5e17-c5ad-28195c884b00","a47dc37b-7693-4d06-b49c-17084b773aff"],"type":"array","items":{"type":"string"}}},"required":["newSortOrder"]},"AnswerProjectQuestionDto":{"type":"object","properties":{"shortTextAnswer":{"type":"string","description":"Short text answer for ShortText question type","example":"This is my answer"},"editorAnswer":{"type":"string","description":"Rich text answer for Editor question type (HTML or Markdown format)","example":"<p>This is a <strong>rich text</strong> answer</p>"},"fileUrls":{"description":"Array of file URLs for File question type","example":["https://example.com/file1.pdf","https://example.com/file2.jpg"],"type":"array","items":{"type":"string"}},"dateAnswer":{"type":"string","description":"Date answer for Datepicker question type (ISO 8601 format)","example":"2024-12-31T00:00:00.000Z"},"selectedOptions":{"description":"Array of selected option IDs for Checkbox question type (multiple selections)","example":["a1b2c3d4-e5f6-7890-abcd-ef1234567890","b2c3d4e5-f6g7-8901-bcde-f12345678901"],"type":"array","items":{"type":"string"}},"selectedOption":{"type":"string","description":"Single selected option ID for Radio question type","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"multiplierAnswer":{"type":"number","description":"Numeric multiplier answer for Number question type","example":2.5},"selectedPersonId":{"description":"Array of person IDs for Person question type","example":["e57dc37b-7693-4d06-b49c-17084b773aff"],"type":"array","items":{"type":"string"}}}},"CreateTestCaseDto":{"type":"object","properties":{"title":{"type":"string","description":"Test case title. Test cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results.","example":"Test login with valid credentials"},"description":{"type":"string","description":"Test case description in HTML or Markdown format","example":"<p>Verify that users can log in with valid credentials.</p>"},"steps":{"type":"string","description":"Test steps in HTML or Markdown format. Provide step-by-step instructions for running the test (e.g., \"1. Navigate to login page 2. Enter credentials 3. Click login\").","example":"<ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>"},"expectedResult":{"type":"string","description":"Expected result in HTML or Markdown format. Describes the expected or correct system behavior when the test is run successfully.","example":"<p>User is successfully logged in and redirected to dashboard.</p>"}},"required":["title","description","steps","expectedResult"]},"UpdateTestCaseDto":{"type":"object","properties":{"title":{"type":"string","description":"Test case title","example":"Test login with valid credentials"},"description":{"type":"string","description":"Test case description in HTML or Markdown format","example":"<p>Verify that users can log in with valid credentials.</p>"},"steps":{"type":"string","description":"Test steps in HTML or Markdown format","example":"<ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>"},"expectedResult":{"type":"string","description":"Expected result in HTML or Markdown format","example":"<p>User is successfully logged in and redirected to dashboard.</p>"}}},"EvaluateTestCaseDto":{"type":"object","properties":{"status":{"type":"string","description":"Test status","enum":["Passed","PassedWithReservations","Failed"],"example":"Passed"},"testComment":{"type":"string","description":"Test comment in HTML or Markdown format (required if status is not Passed)","example":"<p>Found an issue with the login validation.</p>"}},"required":["status"]},"GenerateTasksFromItemsDto":{"type":"object","properties":{"itemType":{"type":"string","description":"Type of item to generate tasks from","example":"ComponentItem","enum":["Component","ComponentItem"]}},"required":["itemType"]},"GenerateTasksFromItemsResponseDto":{"type":"object","properties":{"createdTasksCount":{"type":"number","description":"Number of tasks created","example":5},"componentItemsCount":{"type":"number","description":"Total number of component items processed","example":8},"tasksNumbers":{"description":"Array of task short numbers for created tasks","example":[123,124,125,126,127],"type":"array","items":{"type":"number"}}},"required":["createdTasksCount","componentItemsCount","tasksNumbers"]},"DisconnectItemFromTaskDto":{"type":"object","properties":{"deleteTask":{"type":"boolean","description":"Whether to delete the associated task(s) after disconnecting","example":false}},"required":["deleteTask"]},"PatchProjectComponentDto":{"type":"object","properties":{"name":{"type":"string","description":"Component name","example":"User Authentication Module"},"description":{"type":"string","description":"Component description (HTML or Markdown)","example":"<p>This component handles user authentication and authorization.</p>"},"icon":{"type":"object","description":"Icon identifier (can be set to null)","example":"🔐","nullable":true},"tags":{"description":"Array of tag IDs","example":["550e8400-e29b-41d4-a716-446655440000"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note (HTML or Markdown)","example":"<p>Implementation notes for the development team.</p>"},"discountAmount":{"type":"number","description":"Discount amount to apply to this component. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount in workspace currency. Set to 0 to remove the discount.","example":10,"minimum":0},"discountType":{"type":"string","description":"Type of discount. \"Percentage\" applies a percentage reduction, \"Fixed\" applies a fixed amount reduction. Required when discountAmount is provided.","enum":["Fixed","Percentage"],"example":"Percentage"}}},"UpdateProjectComponentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the updated component","example":"f1a2b3c4-d5e6-7890-abcd-ef1234567890"}},"required":["id"]},"ToggleTodoDto":{"type":"object","properties":{"isDone":{"type":"boolean","description":"Whether the todo should be marked as done (true) or not done (false)","example":true}},"required":["isDone"]},"SortTodosDto":{"type":"object","properties":{"newSortOrder":{"description":"Array of todo item IDs in the desired sort order","example":["a1b2c3d4-e5f6-7890-abcd-ef1234567890","b2c3d4e5-f6g7-8901-bcde-f12345678901"],"type":"array","items":{"type":"string"}}},"required":["newSortOrder"]},"SortTestCasesDto":{"type":"object","properties":{"newSortOrder":{"description":"Array of test case IDs in desired order","example":["e57dc37b-7693-4d06-b49c-17084b773aff","a1b2c3d4-e5f6-7890-abcd-ef1234567890"],"type":"array","items":{"type":"string"}}},"required":["newSortOrder"]},"ExportComponentToLibraryDto":{"type":"object","properties":{"name":{"type":"string","description":"Custom name for the library component. If not provided, uses original component name.","example":"Authentication Module Template"}}},"ExportComponentToLibraryResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the created library component","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}},"required":["id"]},"PatchComponentItemConditionDto":{"type":"object","properties":{"id":{"type":"string","description":"Condition ID for updates. If not provided, creates new condition","example":"550e8400-e29b-41d4-a716-446655440000"},"type":{"type":"string","description":"Condition type. Required if creating new condition","example":"QuestionAnswered","enum":["QuestionAnswered","QuestionNotAnswered","QuestionAnswerContains","QuestionAnswerDoesNotContain","QuestionAnswerEqual","QuestionAnswerDoesNotEqual","TodoIsDone","TodoIsNotDone","TestCasePassed","TestCaseFailed"]},"targetValue":{"type":"string","description":"Target value for condition","example":"Yes"},"targetQuestionId":{"type":"string","description":"Target question ID","example":"550e8400-e29b-41d4-a716-446655440000"},"targetTodoId":{"type":"string","description":"Target todo ID","example":"550e8400-e29b-41d4-a716-446655440000"},"targetTestCaseId":{"type":"string","description":"Target test case ID","example":"550e8400-e29b-41d4-a716-446655440000"},"containsType":{"type":"string","description":"Contains type for condition","example":"Some","enum":["Some","All"]}}},"PatchComponentItemDto":{"type":"object","properties":{"componentId":{"type":"string","description":"ID of the component this item belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"parentId":{"type":"string","description":"ID of parent item for nested items","example":"550e8400-e29b-41d4-a716-446655440000"},"type":{"type":"string","description":"Type of item (Epic, Feature, Task, etc.)","example":"WorkPackage","enum":["Epic","WorkPackage","TodoList","TestSuite"]},"name":{"type":"string","description":"Name of the item","example":"User Authentication Feature"},"icon":{"type":"object","description":"Icon identifier (can be set to null)","example":"🔐","nullable":true},"timeFrame":{"type":"number","description":"Time frame in hours. Required for all types except Epic (when type is provided)","example":40},"description":{"type":"string","description":"Description in HTML or Markdown format (will be converted to IDoc if provided)","example":"<p>Implement user authentication and authorization</p>"},"tags":{"description":"Array of tag UUIDs","example":["550e8400-e29b-41d4-a716-446655440000"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML or Markdown format (will be converted to IDoc if provided)","example":"<p>Implementation notes for the development team</p>"},"includeInSpecification":{"type":"boolean","description":"Whether to include in specification","example":true},"conditions":{"description":"Array of conditions (full array replaces existing conditions)","type":"array","items":{"$ref":"#/components/schemas/PatchComponentItemConditionDto"}}}},"ConnectItemToTaskDto":{"type":"object","properties":{"taskId":{"type":"object","description":"Task ID to connect the item to. Pass null or omit to disconnect the item from any existing task.","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","nullable":true}}},"AddTodoCommentDto":{"type":"object","properties":{"todoComment":{"type":"string","description":"Todo comment content in HTML or Markdown format. Will be converted to internal IDoc format.","example":"<p>This is a comment on the todo item</p>"}},"required":["todoComment"]},"CreateComponentItemDto":{"type":"object","properties":{"componentId":{"type":"string","description":"ID of the component this item belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"parentId":{"type":"string","description":"ID of parent item for nested items","example":"a47dc37b-7693-4d06-b49c-17084b773aff","nullable":true},"type":{"type":"string","description":"Type of component item. Items are the building blocks within components that structure project work:\n- **Epic**: Major sections or phases that group related work packages around a common theme (e.g., \"Technical Implementation\", \"Design & Development\"). Epics help track project progress at a strategic level and can nest other items.\n- **WorkPackage**: Self-contained, clearly defined tasks that deliver tangible results (e.g., \"Set up hosting\", \"Design homepage layout\"). Work packages can have timeframes, questions, todos, and test cases attached.\n- **TodoList**: Checklists of sub-steps or checkpoints perfect for recurring processes, quality control, or project preparation (e.g., \"Kickoff Checklist\", \"Quality Control\"). Each todo item can be checked off individually.\n- **TestSuite**: Formal acceptance tests for project results (e.g., \"Contact Form Tests\", \"SEO Review\"). Contains multiple test cases with steps and expected results. The project is considered finished when all test cases pass.","enum":["Epic","WorkPackage","TodoList","TestSuite"],"example":"WorkPackage"},"name":{"type":"string","description":"Name of the item","example":"User Authentication Feature"},"icon":{"type":"object","description":"Icon identifier","example":"faCheckSquare","nullable":true},"timeFrame":{"type":"number","description":"Time frame in hours representing the estimated effort for completing this item. Required for all item types except Epic. For Work Packages, this represents the estimated time to complete the task. For Todo Lists, this is the estimated time to complete the entire checklist. For Test Suites, this is the estimated time to run all test cases.","example":20},"description":{"type":"string","description":"Description in HTML or Markdown format (will be converted to IDoc)","example":"<p>Implement user authentication with OAuth 2.0</p>"},"tags":{"description":"Array of tag UUIDs","example":["550e8400-e29b-41d4-a716-446655440020"],"type":"array","items":{"type":"string"}},"internalNote":{"type":"string","description":"Internal note in HTML or Markdown format (will be converted to IDoc)","example":"<p>Technical implementation details</p>"},"includeInSpecification":{"type":"boolean","description":"Whether to include in specification","example":true},"conditions":{"description":"Array of conditions that control when this item is visible or active. Conditions link items to questions, todos, or test cases. An item is only shown when all its conditions are met. For example, a work package can be hidden until a specific question is answered a certain way, or a todo list can appear only after a test case passes. Conditions enable dynamic, context-dependent project structures that adapt based on customer answers and project progress.","type":"array","items":{"$ref":"#/components/schemas/CreateComponentItemConditionDto"}}},"required":["componentId","type","name","description","includeInSpecification"]},"CreateComponentItemResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"ID of the created component item","example":"550e8400-e29b-41d4-a716-446655440001"}},"required":["id"]},"ProjectDocumentationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the documentation article (UUID format)","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"ID of the project this documentation article belongs to (UUID format)","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","description":"Title of the documentation article. Should be clear and descriptive to help team members find the article quickly (e.g., \"Access to production system\", \"Prioritizing support cases\")","example":"Getting Started Guide"},"content":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<h1>Introduction</h1><p>This is a documentation article with <strong>formatting</strong>.</p>"},"userId":{"type":"string","description":"User ID of the person who created this documentation article (UUID format). This is automatically set to the authenticated user when the article is created.","example":"550e8400-e29b-41d4-a716-446655440000"},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when the article was created","example":"2024-01-15T10:30:00Z"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp indicating when the article was last updated. This timestamp is automatically updated whenever the article is modified.","example":"2024-01-15T10:30:00Z"}},"required":["id","projectId","title","content","userId","createdAt","updatedAt"]},"CreateProjectDocumentationDto":{"type":"object","properties":{"projectId":{"type":"string","description":"ID of the project this documentation article belongs to. Must be a valid UUID of an existing project in the workspace.","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","description":"Title of the documentation article. Should be clear and descriptive to help team members find the article quickly. Examples: \"Access to production system\", \"Prioritizing support cases\", \"Deployment procedures\".","example":"Getting Started Guide"},"content":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n","example":"<h1>Introduction</h1><p>Content with <strong>formatting</strong>.</p>"}},"required":["projectId","title","content"]},"PatchProjectDocumentationDto":{"type":"object","properties":{"projectId":{"type":"string","description":"ID of the project this documentation article belongs to. If provided, the article will be moved to the specified project. Must be a valid UUID of an existing project in the workspace.","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","description":"Title of the documentation article. If provided, only the title will be updated. Should be clear and descriptive.","example":"Getting Started Guide"},"content":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**codeBlock**\nType: block\nContent: text* (text only (plus marks if applicable), no block wrappers)\nAtts:\n- language: null\n```html\n<pre><code class=\"language-null/some-value\"></code></pre>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n**entity**\nType: inline\nInline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is \"entity\".\n- entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.\n- type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.\n- title: Shown as the visible #label after the chip; should match the real entity title.\n- data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.\nIf the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.\nAtts:\n- entityId: null\n- type: null\n- title: null\n- data: {}\n```html\n<span data-type=\"entity\" entityid=\"null/some-value\" type=\"null/some-value\" title=\"null/some-value\" data=\"[object Object]\">#null/some-value</span>\n```\n\n**appFile**\nType: block\nBlock for a file attachment that already exists in the workspace.\n- fileId: Id from Leadtime file storage. Must be real; do not make up.\n- filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.\nAtts:\n- fileId: \n- filename: \n- size: 0\n```html\n<div data-type=\"appFile\" fileid=\"\" filename=\"\" size=\"0\"></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**mention**\nType: inline\nMentions a workspace member (inserted in the app via the @-picker). The visible label is @name.\n- id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.\n- name: Display name for the @mention label. Should match the user shown for id.\nHTML must keep data-type=\"mention\" with data-id and data-name in sync; plain text in the node is the visible @name.\nAtts:\n- id: null\n- name: null\n```html\n<span data-type=\"mention\" data-name=\"null/some-value\" data-id=\"null/some-value\" id=\"null/some-value\" name=\"null/some-value\">@null/some-value</span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**editorPlaceholder**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nUsed in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.\nAtts: none\n```html\n<placeholder></placeholder>\n```\n\n**appVideo**\nType: block\nBlock for a workspace video file already uploaded in Leadtime (not a generic external embed).\n- fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.\n- filename: Display name; should match the file.\n- width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appVideo\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**embedVideo**\nType: block\nEmbeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).\n- embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.\n- originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.\nAtts:\n- originalUrl: \n- embedUrl: \n```html\n<div originalurl=\"\" embedurl=\"\" data-video-embed=\"\" data-original-url=\"\" data-embed-url=\"\"><iframe src=\"\"></iframe></div>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n If provided, the entire article content will be replaced with this new content.","example":"<h1>Introduction</h1><p>Content with <strong>formatting</strong>.</p>"}}},"ProjectDocumentUserResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the user. Use this ID when assigning the user as a contact person in document creation/update requests.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Full name of the user (first name + last name). Displayed in contact person selection lists and document headers.","example":"John Doe"},"position":{"type":"string","description":"Job title or position of the user within their organization. Displayed alongside the name in contact person selection lists.","example":"Project Manager"},"avatarId":{"type":"object","description":"File ID of the user avatar/profile picture. Null if no avatar is set. Can be used to display user avatars in UI.","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["id","name","position"]},"ProjectDocumentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the document. Used to reference the document in subsequent API calls.","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"object","description":"The title/name of the document. This appears in document lists, headers, and exports. Null for Letter-style Estimate documents (title is not used in letter format).","example":"Q4 2024 Project Estimate"},"description":{"type":"object","description":"Document description or introduction text, returned as HTML (converted from internal IDoc format). Contains rich formatting: headings, lists, tables, links. Null if no description was provided.","example":"<p>Quarterly estimate for client approval</p>"},"projectId":{"type":"string","description":"The ID of the project this document belongs to. Documents are always associated with a specific project.","example":"123e4567-e89b-12d3-a456-426614174000"},"snapshotId":{"type":"string","description":"The project version (snapshot) ID that this document is based on. This ensures the document content is tied to a specific project state for traceability and audit purposes.","example":"123e4567-e89b-12d3-a456-426614174000"},"contactUserId":{"type":"string","description":"The user ID of the contact person assigned to this document. This person appears as the addressed recipient in the document header and recipient fields. Empty string if no contact person is assigned.","example":"123e4567-e89b-12d3-a456-426614174000"},"type":{"type":"string","description":"Document type","enum":["Estimate","Specification","CustomEditor"],"example":"Estimate"},"status":{"type":"string","description":"Document status","enum":["Draft","Final","WaitingForApproval","Accepted","Rejected","Parked"],"example":"Draft"},"includeToc":{"type":"boolean","description":"Include table of contents","example":true},"indicationMode":{"type":"boolean","description":"Enable indication mode","example":false},"titlePage":{"type":"boolean","description":"Include title page","example":true},"headingStyle":{"type":"string","description":"Heading style for document","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential"},"customEditorContent":{"type":"object","description":"Custom document content for CustomEditor document type, returned as HTML (converted from internal IDoc format). Contains rich formatting: headings, lists, tables, links, variables, conditional sections. Null if no custom content was provided.","example":"<h1>Custom Document Content</h1><p>Details here...</p>"},"customVariables":{"description":"Array of custom variables with their current values. Each variable includes its definition (name, type, description, options) and the current value formatted according to the variable type. Empty array if no custom variables are defined.","type":"array","items":{"$ref":"#/components/schemas/DocumentCustomVariableDto"}},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 timestamp indicating when the document was created. Used for sorting, filtering, and audit trails.","example":"2024-01-15T10:30:00Z"},"offerNumber":{"type":"object","description":"Auto-generated offer number (integer). Display format is computed as: [ProjectShortcode]-v[Version]-[DDMMYYYY]-[OfferNumber]. Only populated for Estimate type documents.","example":1},"formattedOfferNumber":{"type":"object","description":"Fully formatted offer number string. Format: [ProjectShortcode]-v[Version]-[DDMMYYYY]-[OfferNumber]. Example: WKM-1-v2-27012026-1. Only populated for Estimate type documents.","example":"WKM-1-v2-27012026-1"},"validUntil":{"type":"object","description":"The date until which this offer is valid. Used for Letter-style offers to indicate expiration date. Null if not set.","example":"2024-02-15T00:00:00Z"},"language":{"type":"object","description":"Document language override (BCP 47 tag). Null when using workspace/organization default.","example":"de"},"documentStyle":{"type":"string","description":"Document layout style","enum":["Letter","Document"],"example":"Letter"},"breakdownLevel":{"type":"string","description":"Component breakdown level","enum":["Complete","UpToEpics","NoBreakdown"],"example":"Complete"},"introText":{"type":"object","description":"Custom introduction text for the offer, returned as HTML (converted from internal IDoc format). Null if not set.","example":"<p>Thank you for your inquiry. We are pleased to submit the following offer:</p>"},"outroText":{"type":"object","description":"Custom closing text for the offer, returned as HTML (converted from internal IDoc format). Null if not set.","example":"<p>This offer is non-binding. We thank you for your interest.</p>"}},"required":["id","projectId","snapshotId","contactUserId","type","status","includeToc","indicationMode","titlePage","customVariables","createdAt","documentStyle","breakdownLevel"]},"CreateProjectDocumentDto":{"type":"object","properties":{"title":{"type":"object","description":"The title/name of the document. This appears in document lists, headers, and exports. Should be descriptive and unique enough to identify the document. For Letter-style Estimate documents, this field is optional and will be automatically cleared (title is not used in letter format).","example":"Q4 2024 Project Estimate"},"description":{"type":"string","description":"Optional document description or introduction text. Accepts HTML or Markdown format - will be automatically converted to internal IDoc format for storage. Supports rich formatting: headings, lists, tables, links, bold, italic. When retrieved via GET, content is returned as HTML for easy display.","example":"<p>Quarterly estimate for client approval</p>"},"snapshotId":{"type":"string","description":"The project version (snapshot) ID that this document is based on. Documents are always linked to a specific project version for traceability and audit purposes. Use GET /projects/:id/versions endpoint to retrieve available versions for a project. The snapshot must exist and belong to the project.","example":"123e4567-e89b-12d3-a456-426614174000"},"contactUserId":{"type":"string","description":"The user ID of the contact person who will receive this document. This person appears as the addressed recipient in the document header and recipient fields. Must be from the list of available contact persons for this project (use GET /projects/:id/documents/contact-persons). For external projects, must be an organization member. For internal projects, must be a workspace employee.","example":"123e4567-e89b-12d3-a456-426614174000"},"type":{"type":"string","description":"The type/category of document. Estimate: Commercial proposals with services, quantities, and costs. Specification: Detailed requirement documents describing deliverables. CustomEditor: Free-form documents like contracts, NDAs, or custom agreements that can use templates with variables.","enum":["Estimate","Specification","CustomEditor"],"example":"Estimate"},"status":{"type":"string","description":"The workflow status of the document. Draft: Initial version, not yet finalized. WaitingForApproval: Sent to client for review. Final: Ready for signing. Accepted: Approved by client. Rejected: Declined by client. Parked: Temporarily set aside. Documents typically start as Draft and progress through the workflow.","enum":["Draft","Final","WaitingForApproval","Accepted","Rejected","Parked"],"example":"Draft"},"includeToc":{"type":"boolean","description":"Whether to include an automatically generated table of contents in the document. The TOC is built from document headings and appears at the beginning of the document. Useful for longer documents with multiple sections. Defaults to true.","example":true,"default":true},"indicationMode":{"type":"boolean","description":"When enabled, marks the document as a non-binding cost indication or preliminary estimate. This is important for legal purposes to distinguish between binding offers and preliminary estimates. When false, the document is treated as a binding offer. Defaults to false.","example":false,"default":false},"titlePage":{"type":"boolean","description":"Whether to include a title page at the beginning of the document. The title page typically includes document title, project information, contact details, and creation date. Useful for formal documents and exports. Defaults to true.","example":true,"default":true},"headingStyle":{"type":"string","description":"Controls how headings are automatically numbered in the document. Normal: No automatic numbering. Sequential: Numbered from level 1 (1, 1.1, 1.1.1). SequentialFromSecondLevel: Numbering starts from level 2 (H1 unnumbered, then 1.1, 1.1.1). SequentialWithParagraphs: Same as SequentialFromSecondLevel but with paragraph sign prefix (§ 1, § 1.1). Important for legal documents and contracts. Numbering is dynamic and adjusts when conditional sections are shown/hidden.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential"},"customEditorContent":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your document templates. Variables are represented as HTML spans:\n\n**workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`\n**company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`\n**organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`\n**project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`\n**currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`\n**recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`\n**macros**: `macros.todaysDate`, `macros.dearCustomer`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span>`, `<span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>`","example":"<h1>Custom Document Content</h1><p>Details here...</p>"},"customVariables":{"description":"Array of custom variables defined for this document. Custom variables allow you to add project- or template-specific values that are not covered by standard system variables (like client name, project budget, contract duration, etc.). Each variable has a name, type, value, and optional description. Variables can be referenced in document templates using the syntax: custom.variable_name. If the document template includes custom variables, you must provide values for all required variables.","type":"array","items":{"$ref":"#/components/schemas/DocumentCustomVariableDto"}},"validUntil":{"format":"date-time","type":"string","description":"The date until which this offer is valid. Used for Letter-style offers to indicate expiration date. Defaults to one month from creation date if not specified.","example":"2024-02-15T00:00:00Z"},"documentStyle":{"type":"string","description":"The document layout style. Letter: Invoice-like layout suitable for direct mailing. Document: Traditional multi-page document with TOC. Only applies to Estimate type documents. Defaults to Letter for new documents.","enum":["Letter","Document"],"example":"Letter"},"breakdownLevel":{"type":"string","description":"Controls how project components are broken down in the document. Complete: Shows all levels (Components → Epics → Work Packages). UpToEpics: Shows Components with totals and Epics with their effort. NoBreakdown: Shows only Components with total price/hours. Applies to both Letter and Document styles.","enum":["Complete","UpToEpics","NoBreakdown"],"example":"Complete"},"introText":{"type":"string","description":"Custom introduction text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.","example":"<p>Thank you for your inquiry. We are pleased to submit the following offer:</p>"},"outroText":{"type":"string","description":"Custom closing text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.","example":"<p>This offer is non-binding. We thank you for your interest.</p>"},"language":{"type":"object","description":"BCP 47 language tag for generated document text (e.g. de, en). When omitted or null, workspace/organization defaults apply.","example":"de"}},"required":["snapshotId","contactUserId","type","status"]},"PatchProjectDocumentDto":{"type":"object","properties":{"title":{"type":"object","description":"The title/name of the document. This appears in document lists, headers, and exports. Should be descriptive and unique enough to identify the document. For Letter-style Estimate documents, this field is optional and will be automatically cleared (title is not used in letter format).","example":"Q4 2024 Project Estimate"},"description":{"type":"string","description":"Optional document description or introduction text. Accepts HTML or Markdown format - will be automatically converted to internal IDoc format for storage. Supports rich formatting: headings, lists, tables, links, bold, italic. When retrieved via GET, content is returned as HTML for easy display.","example":"<p>Quarterly estimate for client approval</p>"},"snapshotId":{"type":"string","description":"The project version (snapshot) ID that this document is based on. Documents are always linked to a specific project version for traceability and audit purposes. Use GET /projects/:id/versions endpoint to retrieve available versions for a project. The snapshot must exist and belong to the project.","example":"123e4567-e89b-12d3-a456-426614174000"},"contactUserId":{"type":"string","description":"The user ID of the contact person who will receive this document. This person appears as the addressed recipient in the document header and recipient fields. Must be from the list of available contact persons for this project (use GET /projects/:id/documents/contact-persons). For external projects, must be an organization member. For internal projects, must be a workspace employee.","example":"123e4567-e89b-12d3-a456-426614174000"},"type":{"type":"string","description":"The type/category of document. Estimate: Commercial proposals with services, quantities, and costs. Specification: Detailed requirement documents describing deliverables. CustomEditor: Free-form documents like contracts, NDAs, or custom agreements that can use templates with variables.","enum":["Estimate","Specification","CustomEditor"],"example":"Estimate"},"status":{"type":"string","description":"The workflow status of the document. Draft: Initial version, not yet finalized. WaitingForApproval: Sent to client for review. Final: Ready for signing. Accepted: Approved by client. Rejected: Declined by client. Parked: Temporarily set aside. Documents typically start as Draft and progress through the workflow.","enum":["Draft","Final","WaitingForApproval","Accepted","Rejected","Parked"],"example":"Draft"},"includeToc":{"type":"boolean","description":"Whether to include an automatically generated table of contents in the document. The TOC is built from document headings and appears at the beginning of the document. Useful for longer documents with multiple sections. Defaults to true.","example":true,"default":true},"indicationMode":{"type":"boolean","description":"When enabled, marks the document as a non-binding cost indication or preliminary estimate. This is important for legal purposes to distinguish between binding offers and preliminary estimates. When false, the document is treated as a binding offer. Defaults to false.","example":false,"default":false},"titlePage":{"type":"boolean","description":"Whether to include a title page at the beginning of the document. The title page typically includes document title, project information, contact details, and creation date. Useful for formal documents and exports. Defaults to true.","example":true,"default":true},"headingStyle":{"type":"string","description":"Controls how headings are automatically numbered in the document. Normal: No automatic numbering. Sequential: Numbered from level 1 (1, 1.1, 1.1.1). SequentialFromSecondLevel: Numbering starts from level 2 (H1 unnumbered, then 1.1, 1.1.1). SequentialWithParagraphs: Same as SequentialFromSecondLevel but with paragraph sign prefix (§ 1, § 1.1). Important for legal documents and contracts. Numbering is dynamic and adjusts when conditional sections are shown/hidden.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential"},"customEditorContent":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n**pageBreak**\nType: block\nPage break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).\nAtts: none\n```html\n<page-break></page-break>\n```\n\n**condition**\nType: block\nContent: conditionBody (a single condition body block)\nThis node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. \n'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.\nAn 'Expression' is { field: string, operator: string, value: any }.\nA 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.\nExample for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.\nSupported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.\nALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.\nAtts:\n- conditions: {\n  \"type\": \"and\",\n  \"expressions\": []\n}\n```html\n<condition conditions=\"{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}\"><condition-body><p class=\"paragraph-base\"></p></condition-body></condition>\n```\n\n**conditionBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<condition-body><p class=\"paragraph-base\"></p></condition-body>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your document templates. Variables are represented as HTML spans:\n\n**workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`\n**company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`\n**organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`\n**project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`\n**currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`\n**recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`\n**macros**: `macros.todaysDate`, `macros.dearCustomer`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"currentUser.firstName\">John</span>`, `<span data-type=\"variable\" data-id=\"project.name\">Website Redesign</span>`","example":"<h1>Custom Document Content</h1><p>Details here...</p>"},"customVariables":{"description":"Array of custom variables defined for this document. Custom variables allow you to add project- or template-specific values that are not covered by standard system variables (like client name, project budget, contract duration, etc.). Each variable has a name, type, value, and optional description. Variables can be referenced in document templates using the syntax: custom.variable_name. If the document template includes custom variables, you must provide values for all required variables.","type":"array","items":{"$ref":"#/components/schemas/DocumentCustomVariableDto"}},"validUntil":{"format":"date-time","type":"string","description":"The date until which this offer is valid. Used for Letter-style offers to indicate expiration date. Defaults to one month from creation date if not specified.","example":"2024-02-15T00:00:00Z"},"documentStyle":{"type":"string","description":"The document layout style. Letter: Invoice-like layout suitable for direct mailing. Document: Traditional multi-page document with TOC. Only applies to Estimate type documents. Defaults to Letter for new documents.","enum":["Letter","Document"],"example":"Letter"},"breakdownLevel":{"type":"string","description":"Controls how project components are broken down in the document. Complete: Shows all levels (Components → Epics → Work Packages). UpToEpics: Shows Components with totals and Epics with their effort. NoBreakdown: Shows only Components with total price/hours. Applies to both Letter and Document styles.","enum":["Complete","UpToEpics","NoBreakdown"],"example":"Complete"},"introText":{"type":"string","description":"Custom introduction text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.","example":"<p>Thank you for your inquiry. We are pleased to submit the following offer:</p>"},"outroText":{"type":"string","description":"Custom closing text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.","example":"<p>This offer is non-binding. We thank you for your interest.</p>"},"language":{"type":"object","description":"BCP 47 language tag for generated document text (e.g. de, en). When omitted or null, workspace/organization defaults apply.","example":"de"}}},"ProjectInterimPaymentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the interim payment (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"ID of the project this payment belongs to (UUID)","example":"550e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Short label or title for the payment (e.g., \"Prepayment\", \"50% Deposit\", \"Milestone 1\")","example":"Advance Payment Q1"},"description":{"type":"string","description":"Detailed description of the payment formatted as HTML. This is automatically converted from the internal IDoc format for API responses. May include notes about when the payment was made, its purpose, or related contract terms.","example":"<p>Payment for Q1 milestone - Client paid €1,000 upfront</p>"},"amount":{"type":"number","description":"Payment amount in the workspace currency. This amount is automatically deducted from the final invoice when the payment is billed.","example":10000},"isBilled":{"type":"boolean","description":"Whether this payment has already been included in an invoice. When true, the payment has been billed and deducted from a final invoice. This field is read-only and managed automatically by the invoicing system.","example":false},"userId":{"type":"string","description":"ID of the user who created this interim payment (UUID)","example":"550e8400-e29b-41d4-a716-446655440002"},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the payment was created","example":"2024-01-15T10:00:00Z"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp when the payment was last updated","example":"2024-01-20T14:30:00Z"},"sort":{"type":"number","description":"Numeric sort order for displaying payments in a list. Lower numbers appear first. Used to organize payments chronologically or by priority.","example":0}},"required":["id","projectId","title","description","amount","isBilled","userId","createdAt","updatedAt","sort"]},"CreateProjectInterimPaymentDto":{"type":"object","properties":{"title":{"type":"string","description":"Short label or title for the payment. Examples: \"Prepayment\", \"50% Deposit\", \"Milestone 1\", \"Advance Payment Q1\". This field is required and cannot be empty.","example":"Advance Payment Q1"},"description":{"type":"string","description":"Optional detailed description or notes about the payment. Can be provided as HTML (e.g., \"<p>Client paid €1,000 upfront</p>\") or Markdown format. The system automatically detects the format and converts it to internal IDoc format for storage. If not provided, the description will be empty.","example":"<p>Payment for Q1 milestone - Client paid €1,000 upfront</p>"},"amount":{"type":"number","description":"Payment amount in the workspace currency. Must be a positive number with a minimum value of 0.01. This amount will be deducted from the final invoice when the payment is billed.","example":10000}},"required":["title","amount"]},"PatchProjectInterimPaymentDto":{"type":"object","properties":{"title":{"type":"string","description":"Short label or title for the payment. If provided, must not be empty. If omitted, the current title remains unchanged.","example":"Updated Payment Title"},"description":{"type":"string","description":"Optional detailed description or notes about the payment. Can be provided as HTML or Markdown format. If omitted, the current description remains unchanged. To clear the description, provide an empty string.","example":"<p>Updated payment description</p>"},"amount":{"type":"number","description":"Payment amount in the workspace currency. If provided, must be a positive number with a minimum value of 0.01. If omitted, the current amount remains unchanged.","example":20000}}},"SortInterimPaymentsDto":{"type":"object","properties":{"paymentIds":{"description":"Array of payment IDs in the desired display order. Each ID must be a valid UUID v4 belonging to the project. The first ID in the array will have sort order 0, the second will have sort order 1, and so on. Payments not included in this array will retain their current sort order. The array cannot be empty.","example":["550e8400-e29b-41d4-a716-446655440000","550e8400-e29b-41d4-a716-446655440001","550e8400-e29b-41d4-a716-446655440002"],"type":"array","items":{"type":"string"}}},"required":["paymentIds"]},"ManualPositionResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the manual position (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Name of the manual position as it appears in quotes and invoices","example":"Consulting Hours"},"description":{"type":"string","description":"Description of the position in HTML format. The description is converted from internal IDoc format to HTML for API responses. May be empty if no description was provided.","example":"<p>Professional consulting services</p>"},"price":{"type":"number","description":"Price of the position in the workspace currency (net price before taxes)","example":150},"categoryId":{"type":"object","description":"Category ID if the position is assigned to a category, or null if no category is assigned. Categories help organize related positions together.","example":"550e8400-e29b-41d4-a716-446655440000","nullable":true},"sort":{"type":"number","description":"Sort order of the position. Lower numbers appear first. Used to control the display order in project planning views, quotes, and invoices.","example":0},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when the position was created","example":"2024-01-01T00:00:00Z"},"discountAmount":{"type":"number","description":"Discount amount applied to this position. 0 means no discount.","example":10},"discountType":{"type":"string","description":"Type of discount: \"Fixed\" (fixed amount) or \"Percentage\" (percentage of price)","enum":["Fixed","Percentage"],"example":"Fixed"}},"required":["id","name","description","price","sort","createdAt","discountAmount","discountType"]},"CreateManualPositionDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the manual position. This will appear in project quotes and invoices. Examples: \"Printing costs\", \"Translation services\", \"Workshop travel expenses\"","example":"Consulting Hours"},"description":{"type":"string","description":"Detailed description of the position. Can be provided as HTML or Markdown - the system will automatically detect and convert the format. This description appears in project quotes and invoices to provide additional context about the service or cost.","example":"<p>Professional consulting services</p>"},"price":{"type":"number","description":"Price of the position in the workspace currency. Must be a non-negative number. This is the net price before taxes. The position can later be discounted if needed.","example":150,"minimum":0},"categoryId":{"type":"string","description":"Optional category ID to organize positions. Categories help group related positions together (e.g., \"External costs\", \"Travel expenses\"). Must be a valid UUID of an existing project manual position category.","example":"550e8400-e29b-41d4-a716-446655440000"}},"required":["name","price"]},"PatchManualPositionDto":{"type":"object","properties":{"name":{"type":"string","description":"Updated name of the manual position. Only provide this field if you want to change the name.","example":"Consulting Hours"},"description":{"type":"string","description":"Updated description. Can be provided as HTML or Markdown - the system will automatically detect and convert the format. Only provide this field if you want to change the description. To clear the description, provide an empty string.","example":"<p>Professional consulting services</p>"},"price":{"type":"number","description":"Updated price in the workspace currency. Must be a non-negative number. Only provide this field if you want to change the price.","example":150,"minimum":0},"categoryId":{"type":"object","description":"Updated category ID. Provide a valid UUID to assign a category, or null to remove the category assignment. Only provide this field if you want to change the category.","example":"550e8400-e29b-41d4-a716-446655440000","nullable":true},"discountAmount":{"type":"number","description":"Discount amount to apply. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount in workspace currency. Set to 0 to remove the discount.","example":10,"minimum":0},"discountType":{"type":"string","description":"Type of discount. \"Percentage\" applies a percentage reduction, \"Fixed\" applies a fixed amount reduction. Required when discountAmount is provided.","enum":["Fixed","Percentage"],"example":"Percentage"}}},"SortManualPositionsDto":{"type":"object","properties":{"positionIds":{"description":"Array of position IDs in the desired display order. The first ID will have sort order 0, the second will have sort order 1, and so on. All IDs must belong to the same project. Positions not included in this array will maintain their current sort order.","example":["550e8400-e29b-41d4-a716-446655440000","550e8400-e29b-41d4-a716-446655440001"],"type":"array","items":{"type":"string"}}},"required":["positionIds"]},"ProjectJournalResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the journal entry (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"The UUID of the project this journal entry belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when the journal entry was created","example":"2024-01-15T10:30:00Z"},"createdBy":{"type":"string","description":"UUID of the user who created this journal entry","example":"550e8400-e29b-41d4-a716-446655440000"},"lastUpdated":{"type":"string","description":"ISO 8601 timestamp indicating when the journal entry was last updated. This is automatically updated whenever any field is modified via PATCH.","example":"2024-01-16T14:20:00Z"},"mood":{"type":"string","description":"Mood indicator for the journal entry. Sad indicates negative reactions, problems, or escalations. Neutral indicates factual observations or open-ended notes. Happy indicates positive feedback, wins, or progress.","enum":["Sad","Neutral","Happy"],"example":"Neutral"},"reminder":{"type":"object","description":"Optional reminder date in ISO 8601 format (YYYY-MM-DD). Set when creating or updating an entry to schedule a follow-up reminder. Null if no reminder is set.","example":"2024-12-31","nullable":true},"body":{"type":"string","description":"The journal entry content converted to HTML format with all formatting preserved. Includes paragraphs, headings, lists, highlighting, and other rich text elements.","example":"<h1>Project Update</h1><p>Today we completed milestone 1.</p>"}},"required":["id","projectId","createdAt","createdBy","lastUpdated","mood","reminder","body"]},"CreateProjectJournalDto":{"type":"object","properties":{"projectId":{"type":"string","description":"The UUID of the project this journal entry belongs to. The project must exist and be accessible to the authenticated user.","example":"550e8400-e29b-41d4-a716-446655440000"},"body":{"type":"string","description":"The journal entry content in HTML or Markdown format. Supports rich text formatting including paragraphs, headings, lists, and highlighting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n This field is required and cannot be empty.","example":"<h1>Project Update</h1><p>Today we completed milestone 1.</p>"},"mood":{"type":"string","description":"Mood indicator for the journal entry. Use Sad for negative reactions, problems, or escalations. Use Neutral for factual observations or open-ended notes. Use Happy for positive feedback, wins, or progress. If not provided, defaults to Neutral.","enum":["Sad","Neutral","Happy"],"example":"Neutral"},"reminder":{"type":"string","description":"Optional reminder date in ISO 8601 format (YYYY-MM-DD). Use this to set a follow-up reminder for the journal entry. Useful for escalation checks, review points, or follow-up tasks.","example":"2024-12-31"}},"required":["projectId","body"]},"PatchProjectJournalDto":{"type":"object","properties":{"projectId":{"type":"string","description":"The UUID of the project this journal entry belongs to. If provided, the entry will be moved to this project. The project must exist and be accessible to the authenticated user. If not provided, the current projectId is preserved.","example":"550e8400-e29b-41d4-a716-446655440000"},"body":{"type":"string","description":"The journal entry content in HTML or Markdown format. Supports rich text formatting including paragraphs, headings, lists, and highlighting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n If provided, must not be empty. If not provided, the current body content is preserved.","example":"<h1>Updated Project Status</h1><p>Progress update...</p>"},"mood":{"type":"string","description":"Mood indicator for the journal entry. Use Sad for negative reactions, problems, or escalations. Use Neutral for factual observations or open-ended notes. Use Happy for positive feedback, wins, or progress. If not provided, the current mood is preserved.","enum":["Sad","Neutral","Happy"],"example":"Happy"},"reminder":{"type":"object","description":"Reminder date in ISO 8601 format (YYYY-MM-DD). Provide a date string to set or update the reminder. Set to null to clear an existing reminder. If not provided (omitted from request), the current reminder is preserved.","example":"2024-12-31","nullable":true}}},"ProjectUploadResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the project upload record (UUID format)","example":"123e4567-e89b-12d3-a456-426614174333"},"projectId":{"type":"string","description":"UUID of the project this upload belongs to. Determines which project the file is associated with.","example":"123e4567-e89b-12d3-a456-426614174000"},"fileId":{"type":"string","description":"UUID of the underlying file in workspace storage. This references the file uploaded via POST /workspace/upload.","example":"123e4567-e89b-12d3-a456-426614174111"},"description":{"type":"object","description":"Free text description for classifying the upload content. Can be null if no description was provided.","example":"Project documentation for Q1 2024","nullable":true},"categoryId":{"type":"object","description":"UUID of the category assigned to this upload for thematic organization. Can be null if no category is assigned.","example":"123e4567-e89b-12d3-a456-426614174222","nullable":true},"fileName":{"type":"string","description":"Original filename of the uploaded file, as stored in the File relation. This is the name the file had when uploaded.","example":"documentation.pdf"},"mimeType":{"type":"string","description":"MIME type of the file indicating its format (e.g., application/pdf, image/png, text/plain). Retrieved from the File relation.","example":"application/pdf"},"fileSize":{"type":"number","description":"Size of the file in bytes. Use this to display file sizes in a human-readable format (e.g., KB, MB).","example":1048576},"userId":{"type":"string","description":"UUID of the user who created this upload. This identifies who uploaded or last updated the file.","example":"123e4567-e89b-12d3-a456-426614174444"},"createdAt":{"type":"string","description":"ISO 8601 formatted timestamp indicating when the upload record was created. Format: YYYY-MM-DDTHH:mm:ss.sssZ","example":"2024-01-01T12:00:00.000Z"},"updatedAt":{"type":"string","description":"ISO 8601 formatted timestamp indicating when the upload record was last updated. Format: YYYY-MM-DDTHH:mm:ss.sssZ. This changes whenever any field of the upload is modified.","example":"2024-01-02T14:30:00.000Z"}},"required":["id","projectId","fileId","description","categoryId","fileName","mimeType","fileSize","userId","createdAt","updatedAt"]},"CreateProjectUploadDto":{"type":"object","properties":{"projectId":{"type":"string","description":"The UUID of the project to attach this upload to. The project must exist in the workspace and the user must have access to it. This determines which project the file will be associated with.","example":"123e4567-e89b-12d3-a456-426614174000"},"fileId":{"type":"string","description":"The UUID of the file obtained from the POST /workspace/upload endpoint. You must upload the file first using that endpoint to receive a fileId, then use that ID here. The file must exist in the workspace and belong to the same workspace.","example":"123e4567-e89b-12d3-a456-426614174111"},"description":{"type":"string","description":"Optional free text description for classifying and identifying the upload content. Use clear, descriptive text like \"Signed contract 10/28/2025\", \"Kickoff protocol\", or \"Technical specification v2\". This helps users quickly understand what the file contains without opening it.","example":"Project documentation for Q1 2024"},"categoryId":{"type":"object","description":"Optional UUID of a category for organizing uploads thematically (e.g., Contract, Offer, Documentation, Dossier). Categories help with filtering, sorting, and finding files. The category must exist in the workspace. Set to null to create an upload without a category. Categories can be created dynamically during upload if they do not exist yet.","example":"123e4567-e89b-12d3-a456-426614174222","nullable":true}},"required":["projectId","fileId"]},"PatchProjectUploadDto":{"type":"object","properties":{"projectId":{"type":"string","description":"Change which project this upload belongs to. Provide the UUID of the target project. The project must exist in the workspace and the user must have access to it. If not provided, the upload remains associated with its current project.","example":"123e4567-e89b-12d3-a456-426614174000"},"description":{"type":"object","description":"Update the description text for the upload. Provide a new description string, or set to null to clear the description. If not provided, the description remains unchanged.","example":"Updated project documentation","nullable":true},"categoryId":{"type":"object","description":"Change the category assignment for the upload. Provide the UUID of a category, or set to null to remove the category. The category must exist in the workspace if provided. If not provided, the category remains unchanged.","example":"123e4567-e89b-12d3-a456-426614174222","nullable":true}}},"GetProjectDocumentSettingsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Project UUID","example":"550e8400-e29b-41d4-a716-446655440000"},"projectDocumentEnableToc":{"type":"object","description":"Enable table of contents for project documents. When true, automatically generated documents include a table of contents. When false, table of contents is disabled for this project. When null, the project inherits the organization/workspace default. This setting is especially helpful for long documents and makes them easier to navigate.","example":true,"nullable":true},"projectDocumentTitlePage":{"type":"object","description":"Enable title page for project documents. When true, generated documents include a title page. When false, title page is disabled for this project. When null, the project inherits the organization/workspace default.","example":true,"nullable":true},"projectDocumentDefaultContactUserId":{"type":"object","description":"Default contact person user ID for project documents. This person is automatically set as the contact when creating new documents for this project. Useful for recurring offer processes, automated preambles, and binding customer-side addressing. When null, the project inherits the organization default (if set) or no default contact is used. Must be a valid user UUID from the workspace.","example":"550e8400-e29b-41d4-a716-446655440001","nullable":true},"projectDocumentHeadingStyle":{"type":"string","description":"Heading style for project documents. Controls the numbering and formatting of headings in generated documents. This feature keeps document outlines consistent, especially in contract documents when dynamic sections appear or disappear based on form parameters. When null, the project inherits the organization/workspace default. Available styles: Normal (no numbering), Sequential (numbering from level 1), SequentialFromSecondLevel (numbering from level 2), SequentialWithParagraphs (numbering with symbol prefix like §).","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential","nullable":true},"projectDocumentEnableTocDefault":{"type":"boolean","description":"Default value for table of contents inherited from organization/workspace hierarchy. This is the value that will be used when projectDocumentEnableToc is null. Shows what the organization or workspace default is set to.","example":false},"projectDocumentTitlePageDefault":{"type":"boolean","description":"Default value for title page inherited from organization/workspace hierarchy. This is the value that will be used when projectDocumentTitlePage is null. Shows what the organization or workspace default is set to.","example":true},"projectDocumentDefaultContactUserIdDefault":{"type":"object","description":"Default contact person user ID inherited from organization hierarchy. This is the value that will be used when projectDocumentDefaultContactUserId is null. When null, there is no default contact person set at the organization/workspace level.","example":"550e8400-e29b-41d4-a716-446655440002","nullable":true},"projectDocumentHeadingStyleDefault":{"type":"string","description":"Default heading style inherited from organization/workspace hierarchy. This is the value that will be used when projectDocumentHeadingStyle is null. Shows what the organization or workspace default heading style is set to.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Normal"}},"required":["id","projectDocumentEnableToc","projectDocumentTitlePage","projectDocumentDefaultContactUserId","projectDocumentHeadingStyle","projectDocumentEnableTocDefault","projectDocumentTitlePageDefault","projectDocumentDefaultContactUserIdDefault","projectDocumentHeadingStyleDefault"]},"PatchProjectDocumentSettingsDto":{"type":"object","properties":{"projectDocumentEnableToc":{"type":"object","description":"Enable table of contents for project documents. Set to true to enable table of contents, false to disable, or null to inherit from organization/workspace default. This setting is especially helpful for long documents and makes them easier to navigate. All fields in this DTO are optional - only provided fields will be updated.","example":true,"nullable":true},"projectDocumentTitlePage":{"type":"object","description":"Enable title page for project documents. Set to true to enable title page, false to disable, or null to inherit from organization/workspace default. All fields in this DTO are optional - only provided fields will be updated.","example":true,"nullable":true},"projectDocumentDefaultContactUserId":{"type":"object","description":"Default contact person user ID for project documents. Set to a valid user UUID to automatically set this person as contact when creating new documents, or null to inherit from organization default (if set) or remove the default contact. Useful for recurring offer processes and automated preambles. Must be a valid UUID of a user in the workspace. All fields in this DTO are optional - only provided fields will be updated.","example":"550e8400-e29b-41d4-a716-446655440001","nullable":true},"projectDocumentHeadingStyle":{"type":"string","description":"Heading style for project documents. Controls the numbering and formatting of headings in generated documents. Set to a specific style to override, or null to inherit from organization/workspace default. Available styles: Normal (no numbering), Sequential (numbering from level 1), SequentialFromSecondLevel (numbering from level 2), SequentialWithParagraphs (numbering with symbol prefix like §). This feature keeps document outlines consistent, especially in contract documents. All fields in this DTO are optional - only provided fields will be updated.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential","nullable":true}}},"ProjectSubscriptionBillingResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the subscription billing item. UUID format.","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"The project this subscription billing belongs to. UUID format.","example":"550e8400-e29b-41d4-a716-446655440001"},"fromDate":{"type":"string","description":"Start date of the subscription period. Format: ISO 8601 date string (YYYY-MM-DD). The subscription becomes active on this date.","example":"2024-01-01"},"toDate":{"type":"string","description":"End date of the subscription period. Format: ISO 8601 date string (YYYY-MM-DD). The subscription expires after this date. Must be equal to or after fromDate.","example":"2024-12-31"},"type":{"type":"string","description":"Type of subscription billing. \"manual\" indicates a manually created subscription (typical for API-created subscriptions). \"product\" indicates a subscription linked to a product catalog item.","enum":["manual","product"],"example":"manual"},"payment":{"type":"string","description":"Payment calculation method for the subscription. \"fixed\" = fixed amount per billing period (e.g., $500/month). \"perUnit\" = variable pricing based on units (e.g., per user, per license). \"oneTime\" = one-time charge within the subscription period.","enum":["fixed","perUnit","oneTime"],"example":"fixed"},"billingTiming":{"type":"string","description":"When billing rows become invoiceable: **arrears** = after the billing period ends (default; row `to` date is before today). **advance** = at the start of the billing period (row `from` date is on or before today).","enum":["arrears","advance"],"example":"arrears"},"manualTitle":{"type":"string","description":"Human-readable title or description of the subscription. This appears on invoices and in project settings. Examples: \"Monthly Support Package\", \"Quarterly Maintenance\", \"Annual License Renewal\".","example":"Monthly Support Package"},"price":{"type":"number","description":"Price amount for the subscription. For fixed payment type, this is the amount per billing period. For perUnit payment type, this is the price per unit. For oneTime payment type, this is the total charge. Must be a positive number.","example":5000},"frequency":{"type":"number","description":"Billing frequency in months. Determines how often billing rows are generated. Common values: 1 = monthly billing, 3 = quarterly billing, 6 = semi-annual billing, 12 = yearly billing. Must be between 1 and 12.","example":1,"minimum":1,"maximum":12},"sort":{"type":"number","description":"Display sort order for the subscription billing item. Lower numbers appear first. Used to control the order when multiple subscriptions are displayed. Automatically calculated when creating new items.","example":0},"quantity":{"type":"number","description":"Quantity for variable pricing. Required when payment type is \"oneTime\". Used to calculate total charge (price × quantity). Optional for other payment types.","example":1}},"required":["id","projectId","fromDate","toDate","type","payment","billingTiming","manualTitle","price","frequency","sort"]},"CreateProjectSubscriptionBillingDto":{"type":"object","properties":{"fromDate":{"type":"string","description":"Start date when the subscription becomes active. Format: ISO 8601 date string (YYYY-MM-DD). The system will generate billing rows starting from the beginning of this month. Required.","example":"2024-01-01"},"toDate":{"type":"string","description":"End date when the subscription expires. Format: ISO 8601 date string (YYYY-MM-DD). The system will generate billing rows up to the end of this month. Must be equal to or after fromDate. Required.","example":"2024-12-31"},"type":{"type":"string","description":"Type of subscription billing. Defaults to \"manual\" for API-created subscriptions. Use \"product\" only if linking to an existing product catalog item. Optional, defaults to manual.","enum":["manual","product"],"example":"manual","default":"manual"},"payment":{"type":"string","description":"Payment calculation method. \"fixed\" = fixed amount per billing period (e.g., $500/month). \"perUnit\" = variable pricing based on units (e.g., $50 per user per month). \"oneTime\" = one-time charge (requires quantity field). Required.","enum":["fixed","perUnit","oneTime"],"example":"fixed"},"billingTiming":{"type":"string","description":"Invoice timing. **arrears** (default) = include a billing row in potential invoices only after the period ends. **advance** = include it from the first day of the billing period.","enum":["arrears","advance"],"example":"arrears","default":"arrears"},"manualTitle":{"type":"string","description":"Human-readable title or description that appears on invoices and in project settings. Examples: \"Monthly Support Package\", \"Quarterly Maintenance Contract\", \"Annual License Renewal\". Required.","example":"Monthly Support Package"},"price":{"type":"number","description":"Price amount. For \"fixed\" payment type: amount per billing period. For \"perUnit\" payment type: price per unit. For \"oneTime\" payment type: total charge amount. Must be a positive number. Required.","example":5000},"frequency":{"type":"number","description":"Billing frequency in months. Determines how often billing rows are generated within the date range. Common values: 1 = monthly, 3 = quarterly, 6 = semi-annual, 12 = yearly. Must be between 1 and 12. Required.","example":1,"minimum":1,"maximum":12},"quantity":{"type":"number","description":"Quantity for variable pricing. Required when payment type is \"oneTime\" to calculate total charge (price × quantity). Optional for \"fixed\" and \"perUnit\" payment types.","example":1}},"required":["fromDate","toDate","payment","manualTitle","price","frequency"]},"PatchProjectSubscriptionBillingDto":{"type":"object","properties":{"fromDate":{"type":"string","description":"Start date when the subscription becomes active. Format: ISO 8601 date string (YYYY-MM-DD). Updating this will regenerate billing rows. If both fromDate and toDate are provided, fromDate must be before or equal to toDate. Optional.","example":"2024-01-01"},"toDate":{"type":"string","description":"End date when the subscription expires. Format: ISO 8601 date string (YYYY-MM-DD). Updating this will regenerate billing rows. If both fromDate and toDate are provided, fromDate must be before or equal to toDate. Optional.","example":"2024-12-31"},"type":{"type":"string","description":"Type of subscription billing. \"manual\" for manually created subscriptions, \"product\" for subscriptions linked to product catalog items. Optional.","enum":["manual","product"],"example":"manual"},"payment":{"type":"string","description":"Payment calculation method. \"fixed\" = fixed amount per billing period. \"perUnit\" = variable pricing based on units. \"oneTime\" = one-time charge (requires quantity). Optional.","enum":["fixed","perUnit","oneTime"],"example":"fixed"},"billingTiming":{"type":"string","description":"Invoice timing: arrears (after period ends) or advance (from period start). Updating changes when unbilled rows qualify for potential invoices; it does not regenerate billing rows.","enum":["arrears","advance"],"example":"advance"},"manualTitle":{"type":"string","description":"Human-readable title or description that appears on invoices and in project settings. Examples: \"Monthly Support Package\", \"Quarterly Maintenance\". Optional.","example":"Monthly Support Package"},"price":{"type":"number","description":"Price amount. For \"fixed\": amount per billing period. For \"perUnit\": price per unit. For \"oneTime\": total charge amount. Must be positive if provided. Optional.","example":5000},"frequency":{"type":"number","description":"Billing frequency in months. Common values: 1 = monthly, 3 = quarterly, 6 = semi-annual, 12 = yearly. Updating this will regenerate billing rows. Must be between 1 and 12 if provided. Optional.","example":1,"minimum":1,"maximum":12},"quantity":{"type":"number","description":"Quantity for variable pricing. Required when payment type is \"oneTime\" to calculate total charge (price × quantity). Optional for other payment types.","example":1}}},"ProjectProductResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the product in the project","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Product name as defined in the product","example":"Product Name"},"finalName":{"type":"object","description":"Final display name combining product name and active variant name (if a variant is active). If no variant is active, this matches the product name.","example":"Variant Name","nullable":true},"categoryId":{"type":"string","description":"UUID of the product category","example":"550e8400-e29b-41d4-a716-446655440001"},"logoId":{"type":"object","description":"UUID of the logo file associated with this product. Null if no logo is set.","example":"550e8400-e29b-41d4-a716-446655440008","nullable":true},"quantity":{"type":"number","description":"Number of units of this product in the project","example":1},"finalPriceFixed":{"type":"object","description":"Final calculated fixed price including variant and option adjustments. This is the price that will be used for billing. Null if the product does not have fixed pricing.","example":100,"nullable":true},"finalPriceSubscription":{"type":"object","description":"Final calculated subscription price including variant and option adjustments. This is the recurring price per billing period. Null if the product does not have subscription pricing.","example":25,"nullable":true},"finalPricePerUnit":{"type":"object","description":"Final calculated per-unit price including variant and option adjustments. This is the price per unit (e.g., per user, per hour). Null if the product does not have per-unit pricing.","example":5,"nullable":true},"activeFrom":{"type":"string","description":"Start date when this product becomes active. Used for time-limited pricing versions. Format: ISO 8601 date (YYYY-MM-DD). Null if no start date is set.","example":"2024-01-01","nullable":true,"format":"date"},"activeTo":{"type":"string","description":"End date when this product becomes inactive. Used for time-limited pricing versions. Format: ISO 8601 date (YYYY-MM-DD). Null if no end date is set.","example":"2024-12-31","nullable":true,"format":"date"},"isActive":{"type":"boolean","description":"Whether the product is currently active based on the activeFrom and activeTo date range. True if current date is within the active range (or no dates are set).","example":true},"fixedDiscountAmount":{"type":"number","description":"Fixed price discount amount (0 = no discount)","example":0},"fixedDiscountType":{"type":"string","description":"Fixed price discount type","enum":["Fixed","Percentage"],"example":"Fixed"},"subscriptionDiscountAmount":{"type":"number","description":"Subscription price discount amount (0 = no discount)","example":0},"subscriptionDiscountType":{"type":"string","description":"Subscription price discount type","enum":["Fixed","Percentage"],"example":"Fixed"},"perUnitDiscountAmount":{"type":"number","description":"Per-unit price discount amount (0 = no discount)","example":0},"perUnitDiscountType":{"type":"string","description":"Per-unit price discount type","enum":["Fixed","Percentage"],"example":"Fixed"}},"required":["id","name","finalName","categoryId","logoId","quantity","finalPriceFixed","finalPriceSubscription","finalPricePerUnit","activeFrom","activeTo","isActive","fixedDiscountAmount","fixedDiscountType","subscriptionDiscountAmount","subscriptionDiscountType","perUnitDiscountAmount","perUnitDiscountType"]},"ProductVariantResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the variant","example":"550e8400-e29b-41d4-a716-446655440006"},"name":{"type":"string","description":"Name of the variant (e.g., Standard, Pro, Enterprise)","example":"Premium"},"description":{"type":"object","description":"Variant description formatted as HTML. Describes what makes this variant different from others.","example":"<p>Premium variant with additional features</p>","nullable":true},"priceFixed":{"type":"object","description":"One-time fixed price for this variant. Null if this variant does not have fixed pricing.","example":150,"nullable":true},"priceSubscription":{"type":"object","description":"Recurring subscription price per billing period for this variant. Null if this variant does not have subscription pricing.","example":50,"nullable":true},"pricePerUnit":{"type":"object","description":"Per-unit price for this variant. Null if this variant does not have per-unit pricing.","example":10,"nullable":true},"isActivated":{"type":"boolean","description":"Whether this variant is currently active for the product. Only one variant can be active at a time.","example":true},"sort":{"type":"number","description":"Sort order of this variant. Lower values appear first when listing variants.","example":0}},"required":["id","name","description","priceFixed","priceSubscription","pricePerUnit","isActivated","sort"]},"ProductOptionValueResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the option value","example":"550e8400-e29b-41d4-a716-446655440007"},"name":{"type":"string","description":"Name of the option value (e.g., Red, Blue, Large, Small)","example":"Color: Red"},"extraPriceFixed":{"type":"object","description":"Additional fixed price added when this option value is selected. Null or 0 if no extra fixed cost.","example":5,"nullable":true},"extraPriceSubscription":{"type":"object","description":"Additional subscription price added per billing period when this option value is selected. Null or 0 if no extra subscription cost.","example":2,"nullable":true},"extraPricePerUnit":{"type":"object","description":"Additional per-unit price added when this option value is selected. Null or 0 if no extra per-unit cost.","example":1,"nullable":true},"isActivated":{"type":"boolean","description":"Whether this option value is currently selected/activated for the product","example":true},"sort":{"type":"number","description":"Sort order of this option value. Lower values appear first when listing option values.","example":0}},"required":["id","name","extraPriceFixed","extraPriceSubscription","extraPricePerUnit","isActivated","sort"]},"ProductOptionResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the option","example":"550e8400-e29b-41d4-a716-446655440004"},"name":{"type":"string","description":"Name of the option (e.g., Color, Size, Add-ons)","example":"Color"},"values":{"description":"Array of all possible values for this option","type":"array","items":{"$ref":"#/components/schemas/ProductOptionValueResponseDto"}},"isRequired":{"type":"boolean","description":"Whether this option must be selected when configuring the product","example":true},"isMultiple":{"type":"boolean","description":"Whether multiple values can be selected from this option","example":false},"sort":{"type":"number","description":"Sort order of this option. Lower values appear first when listing options.","example":0}},"required":["id","name","values","isRequired","isMultiple","sort"]},"ProjectProductDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"UUID of the product in the project","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Product name as defined in the product","example":"Product Name"},"description":{"type":"string","description":"Product description formatted as HTML. Converted from the internal document format.","example":"<p>Product description</p>"},"categoryId":{"type":"string","description":"UUID of the product category","example":"550e8400-e29b-41d4-a716-446655440001"},"logoId":{"type":"object","description":"UUID of the logo file associated with this product. Null if no logo is set.","example":"550e8400-e29b-41d4-a716-446655440008","nullable":true},"quantity":{"type":"number","description":"Number of units of this product in the project","example":1},"priceFixed":{"type":"object","description":"Base fixed price for the product before variant and option adjustments","example":100,"nullable":true},"priceSubscription":{"type":"object","description":"Base subscription price per billing period before variant and option adjustments","example":25,"nullable":true},"pricePerUnit":{"type":"object","description":"Base per-unit price before variant and option adjustments","example":5,"nullable":true},"priceUnitName":{"type":"object","description":"Unit name for per-unit pricing (e.g., per user, per hour)","example":"per user","nullable":true},"priceFrequency":{"type":"object","description":"Billing frequency in months for subscription and per-unit pricing. Valid values: 1, 3, 6, or 12.","example":1,"nullable":true},"finalPriceFixed":{"type":"object","description":"Final calculated fixed price including variant and option adjustments. This is the price used for billing.","example":100,"nullable":true},"finalPriceSubscription":{"type":"object","description":"Final calculated subscription price including variant and option adjustments. This is the recurring price per billing period.","example":25,"nullable":true},"finalPricePerUnit":{"type":"object","description":"Final calculated per-unit price including variant and option adjustments","example":5,"nullable":true},"finalName":{"type":"object","description":"Final display name combining product name and active variant name (if a variant is active)","example":"Variant Name","nullable":true},"activeFrom":{"type":"string","description":"Start date when this product becomes active. Format: ISO 8601 date (YYYY-MM-DD). Null if no start date is set.","example":"2024-01-01","nullable":true,"format":"date"},"activeTo":{"type":"string","description":"End date when this product becomes inactive. Format: ISO 8601 date (YYYY-MM-DD). Null if no end date is set.","example":"2024-12-31","nullable":true,"format":"date"},"variants":{"description":"Array of all available variants for this product","type":"array","items":{"$ref":"#/components/schemas/ProductVariantResponseDto"}},"options":{"description":"Array of all available options for this product","type":"array","items":{"$ref":"#/components/schemas/ProductOptionResponseDto"}}},"required":["id","name","description","categoryId","logoId","quantity","priceFixed","priceSubscription","pricePerUnit","priceUnitName","priceFrequency","finalPriceFixed","finalPriceSubscription","finalPricePerUnit","finalName","activeFrom","activeTo","variants","options"]},"ImportProductToProjectDto":{"type":"object","properties":{"productId":{"type":"string","description":"UUID of the catalog product to import into the project. The product must exist in the product catalog. When imported, a copy is created in the project that can be configured independently.","example":"550e8400-e29b-41d4-a716-446655440002"},"quantity":{"type":"number","description":"Number of units of this product to add to the project. Must be at least 1. The quantity affects the final price calculation when using per-unit pricing.","example":2,"minimum":1},"activeVariantId":{"type":"object","description":"UUID of the variant to activate for this product instance. Variants represent different configurations or tiers of a product (e.g., Standard, Pro, Enterprise). If not provided, no variant is active. The variant must belong to the product being imported.","example":"550e8400-e29b-41d4-a716-446655440003","nullable":true},"options":{"description":"Array of option selections for this product. Options are add-ons or configuration choices that customers can select. Each option selection specifies which option is being configured and which values are selected. Required options must be included. Can be an empty array if no options are needed.","example":[{"id":"550e8400-e29b-41d4-a716-446655440004","value":["550e8400-e29b-41d4-a716-446655440005"]}],"type":"array","items":{"$ref":"#/components/schemas/ProductSettingOptionDto"}},"priceFrequency":{"type":"number","description":"Billing frequency in months for subscription or per-unit pricing. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).","example":1},"contractPeriodMonths":{"type":"number","description":"Contract period in months. Defaults to 12 months if not specified.","example":12},"renewalMode":{"type":"string","description":"Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period.","enum":["Automatic","Stops"],"example":"Automatic"},"activeFromType":{"type":"string","description":"Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.","enum":["Immediately","ProjectBilling","FixedDate"],"example":"Immediately"},"activeFromFixedDate":{"type":"string","description":"Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.","example":"2024-01-01","nullable":true,"format":"date"},"customPrices":{"type":"object","description":"Custom price overrides for base product, variants, and options. Keys can be \"base\", \"variant_<variantId>\", or \"option_<optionValueId>\". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving.","additionalProperties":{"$ref":"#/components/schemas/CustomPriceDto"},"example":{"base":{"priceFixed":100.5,"priceSubscription":50.25,"pricePerUnit":10},"variant_550e8400":{"priceFixed":150,"priceSubscription":75,"pricePerUnit":15}}}},"required":["productId","quantity","options","customPrices"]},"ProductSettingsDto":{"type":"object","properties":{"quantity":{"type":"number","description":"Number of units of this product in the project. Must be at least 1. This affects the final price calculation, especially for per-unit pricing.","example":3,"minimum":1},"activeVariantId":{"type":"object","description":"UUID of the variant to activate for this product. Variants represent different configurations or tiers (e.g., Standard, Pro, Enterprise). Set to null to deactivate any currently active variant. The variant must belong to this product.","example":"550e8400-e29b-41d4-a716-446655440006","nullable":true},"options":{"description":"Array of option selections for this product. Each selection specifies which option is being configured and which values are selected. Required options must be included. Provide an empty array if no options should be selected.","example":[{"id":"550e8400-e29b-41d4-a716-446655440004","value":["550e8400-e29b-41d4-a716-446655440007"]}],"type":"array","items":{"$ref":"#/components/schemas/ProductSettingOptionDto"}},"priceFrequency":{"type":"number","description":"Billing frequency in months for subscription or per-unit pricing. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).","example":1},"contractPeriodMonths":{"type":"number","description":"Contract period in months. Defaults to 12 months if not specified.","example":12},"renewalMode":{"type":"string","description":"Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period.","enum":["Automatic","Stops"],"example":"Automatic"},"activeFromType":{"type":"string","description":"Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.","enum":["Immediately","ProjectBilling","FixedDate"],"example":"Immediately"},"activeFromFixedDate":{"type":"string","description":"Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.","example":"2024-01-01","nullable":true,"format":"date"},"customPrices":{"type":"object","description":"Custom price overrides for base product, variants, and options. Keys can be \"base\", \"variant_<variantId>\", or \"option_<optionValueId>\". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving.","additionalProperties":{"$ref":"#/components/schemas/CustomPriceDto"},"example":{"base":{"priceFixed":100.5,"priceSubscription":50.25,"pricePerUnit":10},"variant_550e8400":{"priceFixed":150,"priceSubscription":75,"pricePerUnit":15}}},"fixedDiscountAmount":{"type":"number","description":"Discount amount for the fixed price component. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount. Set to 0 to remove.","example":10,"minimum":0},"fixedDiscountType":{"type":"string","description":"Discount type for the fixed price component.","enum":["Fixed","Percentage"]},"subscriptionDiscountAmount":{"type":"number","description":"Discount amount for the subscription price component.","example":15,"minimum":0},"subscriptionDiscountType":{"type":"string","description":"Discount type for the subscription price component.","enum":["Fixed","Percentage"]},"perUnitDiscountAmount":{"type":"number","description":"Discount amount for the per-unit price component.","example":5,"minimum":0},"perUnitDiscountType":{"type":"string","description":"Discount type for the per-unit price component.","enum":["Fixed","Percentage"]}},"required":["quantity","options","customPrices"]},"ProductSortDto":{"type":"object","properties":{"ids":{"description":"Array of product UUIDs in the desired display order. The first UUID in the array will appear first in the product list, the second UUID will appear second, and so on. All product IDs must belong to the same project. Products not included in this array will retain their current sort order.","example":["550e8400-e29b-41d4-a716-446655440008","550e8400-e29b-41d4-a716-446655440009","550e8400-e29b-41d4-a716-446655440010"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"ProjectVersionDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the version (UUID)","example":"660e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Title/name of the version as provided when creating it","example":"Version 1.2.3"},"semverVersion":{"type":"string","description":"Semantic version string automatically generated based on parent version and branch. Follows semver-like format (e.g., \"1.2.3\")","example":"1.2.3"},"branchId":{"type":"string","description":"Branch identifier (UUID) for version tracking. Used to group related versions together and enable branching/versioning workflows","example":"770e8400-e29b-41d4-a716-446655440002"},"parentId":{"type":"object","description":"Parent version ID (UUID). Null if this is the first version created for the project. Used to track version lineage and generate semantic version numbers","example":"660e8400-e29b-41d4-a716-446655440000","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the version was created","example":"2024-01-15T10:30:00.000Z"},"createdBy":{"type":"string","description":"User ID (UUID) who created the version","example":"880e8400-e29b-41d4-a716-446655440003"},"description":{"type":"string","description":"HTML-formatted description of the version. Originally provided as HTML or Markdown when creating the version, always returned as HTML","example":"<h1>Version Description</h1><p>This is version 1.2.3</p>"},"isCurrent":{"type":"boolean","description":"Whether this is the current active version for the project. Only one version can be current at a time. The current version represents the active project state","example":true}},"required":["id","title","semverVersion","branchId","parentId","createdAt","createdBy","description","isCurrent"]},"ProjectVersionDetailsDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the version (UUID)","example":"660e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Title/name of the version as provided when creating it","example":"Version 1.2.3"},"semverVersion":{"type":"string","description":"Semantic version string automatically generated based on parent version and branch. Follows semver-like format (e.g., \"1.2.3\")","example":"1.2.3"},"branchId":{"type":"string","description":"Branch identifier (UUID) for version tracking. Used to group related versions together and enable branching/versioning workflows","example":"770e8400-e29b-41d4-a716-446655440002"},"parentId":{"type":"object","description":"Parent version ID (UUID). Null if this is the first version created for the project. Used to track version lineage and generate semantic version numbers","example":"660e8400-e29b-41d4-a716-446655440000","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the version was created","example":"2024-01-15T10:30:00.000Z"},"createdBy":{"type":"string","description":"User ID (UUID) who created the version","example":"880e8400-e29b-41d4-a716-446655440003"},"description":{"type":"string","description":"HTML-formatted description of the version. Originally provided as HTML or Markdown when creating the version, always returned as HTML","example":"<h1>Version Description</h1><p>This is version 1.2.3</p>"},"isCurrent":{"type":"boolean","description":"Whether this is the current active version for the project. Only one version can be current at a time. The current version represents the active project state","example":true},"estimateData":{"type":"object","description":"Complete estimate data captured at the time the version was created. Includes products with variants and prices, components with nested structure, manual positions, and pricing breakdowns (fixed, subscriptions, per-unit). This data represents the exact project state when the version was saved."}},"required":["id","title","semverVersion","branchId","parentId","createdAt","createdBy","description","isCurrent","estimateData"]},"CreateProjectVersionDto":{"type":"object","properties":{"projectId":{"type":"string","description":"UUID of the project to create a version for","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","description":"Title/name of the version. Use clear, descriptive titles like \"Version 1.2.3 - Initial Release\" or \"Customer Proposal v2\"","example":"Version 1.2.3 - Initial Release"},"description":{"type":"string","description":"Description of the version in HTML or Markdown format (optional). This can include notes about what changed, why the version was created, or any relevant context.","example":"<h1>Version 1.2.3</h1><p>This version includes initial features</p>"}},"required":["projectId","title"]},"ProjectConfigurationStateDto":{"type":"object","properties":{"projectId":{"type":"string","description":"Project UUID","example":"550e8400-e29b-41d4-a716-446655440000"},"lastConfigurationSnapshot":{"type":"object","description":"Information about the last saved version (snapshot). Null if no versions have been created yet. Contains the version ID, semantic version string, creation timestamp, and title.","nullable":true,"properties":{"id":{"type":"string","description":"Version ID (UUID)","example":"660e8400-e29b-41d4-a716-446655440001"},"version":{"type":"string","description":"Semantic version string","example":"1.2.3"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when version was created","example":"2024-01-15T10:30:00.000Z"},"title":{"type":"string","description":"Version title","example":"Version 1.2.3"}}},"configurationChanged":{"type":"boolean","description":"Whether the project configuration has changed since the last version was saved. True means there are unsaved changes that should be versioned before proceeding with offers or documents.","example":false},"configurationChangedAt":{"type":"object","description":"ISO 8601 timestamp when the configuration last changed. Null if configuration has not changed since the last version was saved.","example":null,"nullable":true},"productCount":{"type":"number","description":"Number of products currently in the project","example":5},"componentCount":{"type":"number","description":"Number of components currently in the project","example":10},"manualPositionCount":{"type":"number","description":"Number of manual positions currently in the project","example":2},"discountCount":{"type":"number","description":"Number of discounts currently applied to the project","example":1},"snapshotCount":{"type":"number","description":"Total number of versions (snapshots) saved for the project","example":3},"documentCount":{"type":"number","description":"Number of documents associated with the project","example":0}},"required":["projectId","lastConfigurationSnapshot","configurationChanged","configurationChangedAt","productCount","componentCount","manualPositionCount","discountCount","snapshotCount","documentCount"]},"ProjectCategoryDto":{"type":"object","properties":{"id":{"type":"string","description":"Project category ID"},"name":{"type":"string","description":"Project category name"},"icon":{"type":"string","description":"Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:wrench:`, `:briefcase:`)"},"description":{"type":"object","description":"Category description"},"translations":{"description":"Translations","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}}},"required":["id","name","icon","translations"]},"ProjectStatusDto":{"type":"object","properties":{"id":{"type":"string","description":"Project status ID"},"name":{"type":"string","description":"Project status name"},"icon":{"type":"string","description":"Icon identifier in the format `:icon_name:` (e.g., `:hourglass_flowing_sand:`, `:check_mark:`, `:moneybag:`)"},"description":{"type":"object","description":"Status description"},"type":{"type":"string","enum":["Sales","Requirements","Implementation","QualityManagement","Billing","Done"],"description":"Project status type"},"translations":{"description":"Translations","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}}},"required":["id","name","icon","type","translations"]},"ProjectPhaseDto":{"type":"object","properties":{"id":{"type":"string","description":"Project phase ID"},"name":{"type":"string","description":"Project phase name"},"icon":{"type":"string","description":"Icon identifier in the format `:icon_name:` (e.g., `:calendar:`, `:phone:`, `:trophy:`)"},"description":{"type":"object","description":"Phase description"},"type":{"type":"string","enum":["new","inProgress","done"],"description":"Project phase type"},"translations":{"description":"Translations","type":"array","items":{"$ref":"#/components/schemas/TranslationDto"}}},"required":["id","name","icon","type","translations"]},"ProjectResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Project ID","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Project name","example":"Website Redesign"},"shortName":{"type":"string","description":"Project short name (organization prefix + number)","example":"ACME-123"},"shortNumber":{"type":"number","description":"Project short number","example":123},"type":{"type":"string","description":"Project type","enum":["Single","Support"],"example":"Single"},"valueGroup":{"type":"string","description":"Value group classification","enum":["DirectValue","IndirectValue","NonValue","Waste"],"example":"DirectValue"},"description":{"type":"string","description":"Project description (HTML)","example":"<h1>Project Description</h1><p>This is a comprehensive project.</p>"},"icon":{"type":"object","description":"Icon/emoji","example":"🚀","nullable":true},"categoryId":{"type":"string","description":"ID of the project category","example":"550e8400-e29b-41d4-a716-446655440001"},"statusId":{"type":"string","description":"ID of the project status","example":"550e8400-e29b-41d4-a716-446655440002"},"phaseId":{"type":"object","description":"ID of the project phase","example":"550e8400-e29b-41d4-a716-446655440003","nullable":true},"organizationId":{"type":"object","description":"ID of the organization","example":"550e8400-e29b-41d4-a716-446655440004","nullable":true},"organization":{"type":"object","description":"Organization details","additionalProperties":false,"properties":{"name":{"type":"string"},"shortName":{"type":"string"},"color":{"type":"string"}},"example":{"name":"ACME Corporation","shortName":"ACME","color":"#FF5733"},"nullable":true},"defaultAccountableId":{"type":"object","description":"ID of the default accountable user","example":"550e8400-e29b-41d4-a716-446655440005","nullable":true},"responsibleId":{"type":"object","description":"ID of the responsible user","example":"550e8400-e29b-41d4-a716-446655440006","nullable":true},"guestAccess":{"type":"boolean","description":"Whether guest access is enabled","example":false},"deadline":{"type":"object","description":"Project deadline","example":"2024-12-31T23:59:59.000Z","nullable":true},"enableExpressQuotations":{"type":"boolean","description":"Whether express quotations are enabled","example":false},"createdAt":{"format":"date-time","type":"string","description":"Creation timestamp","example":"2024-01-01T00:00:00.000Z"},"editedAt":{"format":"date-time","type":"string","description":"Last edit timestamp","example":"2024-01-15T10:30:00.000Z"},"archivedAt":{"type":"object","description":"Archived timestamp","example":null,"nullable":true},"users":{"description":"Array of user IDs assigned to the project","example":["550e8400-e29b-41d4-a716-446655440007","550e8400-e29b-41d4-a716-446655440008"],"type":"array","items":{"type":"string"}},"teams":{"description":"Array of team IDs assigned to the project","example":["550e8400-e29b-41d4-a716-446655440009"],"type":"array","items":{"type":"string"}},"taskTypes":{"description":"Array of task type IDs enabled for the project","example":["550e8400-e29b-41d4-a716-44665544000a","550e8400-e29b-41d4-a716-44665544000b"],"type":"array","items":{"type":"string"}},"activities":{"description":"Array of time activity IDs enabled for the project","example":["550e8400-e29b-41d4-a716-44665544000c","550e8400-e29b-41d4-a716-44665544000d"],"type":"array","items":{"type":"string"}},"customFields":{"type":"object","description":"Custom fields as key-value pairs","additionalProperties":true,"example":{"customField1":"value1","customField2":123}},"tags":{"description":"Array of tags","example":["urgent","high-priority"],"type":"array","items":{"type":"string"}},"color":{"type":"string","description":"Project color (from organization or workspace default)","example":"#FF5733"},"isFavorite":{"type":"boolean","description":"Whether the project is marked as favorite by the current user","example":false}},"required":["id","name","shortName","shortNumber","type","valueGroup","description","icon","categoryId","statusId","phaseId","organizationId","organization","defaultAccountableId","responsibleId","guestAccess","deadline","enableExpressQuotations","createdAt","editedAt","archivedAt","users","teams","taskTypes","activities","customFields","tags","color","isFavorite"]},"CreateProjectDto":{"type":"object","properties":{"name":{"type":"string","description":"Full name of the project. This is the primary display name used throughout the application.","example":"Website Redesign"},"type":{"type":"string","description":"Project type determines the project structure and workflow. Single projects are one-off initiatives with defined start and finish, usually planned and billed as quoted. Ongoing projects are continuous activities where tasks are handled and billed based on effort.","enum":["Single","Support"],"example":"Single"},"valueGroup":{"type":"string","description":"Value group classification indicates how the project contributes to value creation. A - Direct value creation (external customer projects), B - Indirect value creation (internal projects that increase product/service value), C - Not value creating (administrative tasks), D - Waste (unproductive time).","enum":["DirectValue","IndirectValue","NonValue","Waste"],"example":"DirectValue"},"categoryId":{"type":"string","description":"UUID of the project category. Categories classify projects by functional area or project type (e.g., Onboarding, Support, Implementation). Use the GET /projects/categories endpoint to retrieve available categories.","example":"550e8400-e29b-41d4-a716-446655440000"},"statusId":{"type":"string","description":"UUID of the project status. Statuses represent the current progress stage of a project (e.g., Sales, Implementation, Billing, Done). Use the GET /projects/statuses endpoint to retrieve available statuses.","example":"550e8400-e29b-41d4-a716-446655440001"},"phaseId":{"type":"object","description":"UUID of the project phase. Phases represent distinct stages within a project lifecycle (e.g., Planning, Development, Testing). Required for external single projects, optional otherwise. Use the GET /projects/phases endpoint to retrieve available phases.","example":"550e8400-e29b-41d4-a716-446655440002"},"organizationId":{"type":"object","description":"UUID of the organization (client). Required for external projects - must be set to link the project to a client organization. Must be null for internal projects. Use the GET /organizations endpoint to retrieve available organizations.","example":"550e8400-e29b-41d4-a716-446655440003"},"description":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n The description will be automatically converted from HTML/Markdown to the internal IDoc format. When retrieved later via GET, it will be returned as HTML. Also accepts ProseMirror IDoc JSON (object or string).","example":"<h1>Project Description</h1><p>This is a comprehensive project for redesigning our company website.</p>"},"icon":{"type":"string","description":"Visual identifier for the project. Must be in the format `:icon_name:` (e.g., `:rocket:`, `:shopping_cart:`, `:mobile_phone:`). This icon is displayed in project lists and throughout the application. You can use standard emoji short names (e.g., `:rocket:` for 🚀, `:shopping_cart:` for 🛒) or custom icon names created in the workspace.","example":":rocket:"},"guestAccess":{"type":"boolean","description":"Whether organization members (guests) can access this project. Only applicable for external (client) projects. When enabled, members of the linked organization can view and interact with the project and its tasks.","example":false,"default":false},"deadline":{"type":"object","description":"Project deadline date in ISO 8601 format (e.g., \"2024-12-31T23:59:59.000Z\"). Optional target completion date for the project.","example":"2024-12-31T23:59:59.000Z"},"defaultAccountableId":{"type":"object","description":"UUID of the user who is accountable for the project by default. This user will be set as accountable on new tasks created in this project unless overridden. Use the GET /workspace/users endpoint to retrieve available users.","example":"550e8400-e29b-41d4-a716-446655440004"},"responsibleId":{"type":"object","description":"UUID of the user who is responsible for the project (e.g., project manager, sales manager). Use the GET /workspace/users endpoint to retrieve available users.","example":"550e8400-e29b-41d4-a716-446655440005"},"users":{"description":"Array of user UUIDs who have access to this project. These users can create, edit, and log time on tasks in the project. At least one user must be assigned. Use the GET /workspace/users endpoint to retrieve available users.","example":["550e8400-e29b-41d4-a716-446655440006","550e8400-e29b-41d4-a716-446655440007"],"type":"array","items":{"type":"string"}},"teams":{"description":"Array of team UUIDs assigned to the project. All members of these teams will have access to the project. Use the GET /teams endpoint to retrieve available teams.","example":["550e8400-e29b-41d4-a716-446655440008"],"type":"array","items":{"type":"string"}},"taskTypes":{"description":"Array of task type UUIDs that can be used in this project (e.g., Feature, Bug, Management, Server issue). At least one task type must be specified. Use the GET /task-types endpoint to retrieve available task types.","example":["550e8400-e29b-41d4-a716-446655440009","550e8400-e29b-41d4-a716-44665544000a"],"type":"array","items":{"type":"string"}},"activities":{"description":"Array of time activity UUIDs that can be used when logging time in this project (e.g., Development, Testing, Management, Research). At least one activity must be specified. Use the GET /activities endpoint to retrieve available activities.","example":["550e8400-e29b-41d4-a716-44665544000b","550e8400-e29b-41d4-a716-44665544000c"],"type":"array","items":{"type":"string"}},"customFields":{"type":"object","description":"Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /custom-fields endpoint to retrieve available custom fields and their configurations.","additionalProperties":true,"example":{"customField1":"value1","customField2":123}},"tags":{"description":"Array of tag strings for categorizing and filtering projects. Tags can represent topics, teams, or work types. Tags make it easier to filter and organize projects in the project overview.","example":["urgent","high-priority"],"type":"array","items":{"type":"string"}}},"required":["name","type","valueGroup","categoryId","statusId","description","guestAccess","users","teams","taskTypes","activities","customFields"]},"PatchProjectDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the project","example":"Website Redesign v2"},"type":{"type":"string","description":"Project type - determines if project is internal or client-facing","enum":["Single","Support"],"example":"Single"},"valueGroup":{"type":"string","description":"Value group classification for the project","enum":["DirectValue","IndirectValue","NonValue","Waste"],"example":"DirectValue"},"categoryId":{"type":"string","description":"ID of the project category","example":"550e8400-e29b-41d4-a716-446655440000"},"statusId":{"type":"string","description":"ID of the project status","example":"550e8400-e29b-41d4-a716-446655440001"},"phaseId":{"type":"object","description":"ID of the project phase","example":"550e8400-e29b-41d4-a716-446655440002"},"organizationId":{"type":"object","description":"ID of the organization (required for client projects, must be null for internal projects)","example":"550e8400-e29b-41d4-a716-446655440003"},"description":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n Also accepts ProseMirror IDoc JSON (object or string).","example":"<h1>Updated Description</h1><p>This is the updated project description.</p>"},"icon":{"type":"string","description":"Icon/emoji for the project","example":"🎯"},"guestAccess":{"type":"boolean","description":"Whether guest access is enabled (only applicable for client projects)","example":true,"default":false},"deadline":{"type":"object","description":"Project deadline date","example":"2024-12-31T23:59:59.000Z"},"defaultAccountableId":{"type":"object","description":"ID of the default accountable user","example":"550e8400-e29b-41d4-a716-446655440004"},"responsibleId":{"type":"object","description":"ID of the responsible user","example":"550e8400-e29b-41d4-a716-446655440005"},"users":{"description":"Array of user IDs assigned to the project","example":["550e8400-e29b-41d4-a716-446655440006","550e8400-e29b-41d4-a716-446655440007"],"type":"array","items":{"type":"string"}},"teams":{"description":"Array of team IDs assigned to the project","example":["550e8400-e29b-41d4-a716-446655440008"],"type":"array","items":{"type":"string"}},"taskTypes":{"description":"Array of task type IDs enabled for the project","example":["550e8400-e29b-41d4-a716-446655440009","550e8400-e29b-41d4-a716-44665544000a"],"type":"array","items":{"type":"string"}},"activities":{"description":"Array of time activity IDs enabled for the project","example":["550e8400-e29b-41d4-a716-44665544000b","550e8400-e29b-41d4-a716-44665544000c"],"type":"array","items":{"type":"string"}},"customFields":{"type":"object","description":"Custom fields as key-value pairs","additionalProperties":true,"example":{"customField1":"updated value","customField2":456}},"tags":{"description":"Array of tags","example":["urgent","high-priority","review-needed"],"type":"array","items":{"type":"string"}},"enableExpressQuotations":{"type":"boolean","description":"Whether express quotations are enabled for this project. Express quotations allow quick pricing of tasks without full project planning. Only applicable for external projects.","example":false}},"required":["customFields"]},"SupportContingentWithDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the support contingent (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"ID of the project this contingent belongs to (UUID)","example":"550e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Title or name of the support contingent plan","example":"Maintenance flatrate"},"fromDate":{"type":"string","description":"Start date of the contingent period (ISO 8601 date format)","example":"2025-01-01"},"toDate":{"type":"string","description":"End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.","example":"2025-12-31"},"frequency":{"type":"number","description":"Frequency in months for the contingent period","example":12},"hours":{"type":"number","description":"Total hours allocated for the contingent period","example":120},"transferable":{"type":"boolean","description":"Whether unused hours can be transferred to the next period","example":false},"isActive":{"type":"boolean","description":"Whether the contingent is currently active (today is within fromDate and toDate)","example":true},"availableHours":{"type":"number","description":"Available hours for the current period (calculated based on transferable logic and consumed hours)","example":85.5},"consumedHours":{"type":"number","description":"Consumed hours in the current period (from invoices)","example":34.5},"potentiallyUsedByTasks":{"type":"number","description":"Hours that will be potentially used by unbilled tasks (calculated based on task assignment logic)","example":17.5},"availableByContract":{"type":"number","description":"Available hours from the current period contract (baseHours - consumed in current period)","example":40},"transferableFromPrevious":{"type":"number","description":"Transferable hours from previous periods (only applicable for transferable contingents)","example":129},"currentPeriodStart":{"type":"string","description":"Start date of the current period (ISO 8601 date format)","example":"2025-11-01"},"currentPeriodEnd":{"type":"string","description":"End date of the current period (ISO 8601 date format)","example":"2025-11-30"},"currentPeriodHasNoEndDate":{"type":"boolean","description":"Flag indicating if the current period has no end date (for frequency=0 contingents with null toDate). When true, currentPeriodEnd represents the current date for display purposes, but the period is actually open-ended.","example":false},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the contingent was created","example":"2024-01-15T10:00:00Z"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp when the contingent was last updated","example":"2024-01-20T14:30:00Z"}},"required":["id","projectId","title","fromDate","frequency","hours","transferable","isActive","availableHours","consumedHours","potentiallyUsedByTasks","availableByContract","transferableFromPrevious","createdAt","updatedAt"]},"SupportContingentPeriodTaskDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the task (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"shortNumber":{"type":"number","description":"Short number of the task","example":1234},"title":{"type":"string","description":"Title of the task","example":"Fix bug in payment module"},"hoursBilled":{"type":"number","description":"Hours billed/consumed for this task","example":5.5},"invoiceNumber":{"type":"object","description":"Invoice number if the task is billed, null if unbilled","example":"INV-2025-001"},"isUnbilled":{"type":"boolean","description":"Flag indicating if this task is unbilled (in progress)","example":false}},"required":["id","shortNumber","title","hoursBilled"]},"SupportContingentPeriodDto":{"type":"object","properties":{"periodStart":{"type":"string","description":"Start date of the period (ISO 8601 date format)","example":"2025-11-01"},"periodEnd":{"type":"string","description":"End date of the period (ISO 8601 date format)","example":"2025-11-30"},"hours":{"type":"number","description":"Hours allocated for this period","example":40},"hoursFromPrevious":{"type":"number","description":"Hours transferred from previous period (only applicable for transferable contingents)","example":10},"consumedHours":{"type":"number","description":"Hours consumed in this period (from invoices)","example":15.5},"isCurrentPeriod":{"type":"boolean","description":"Flag indicating if this is the current active period","example":true},"hasNoEndDate":{"type":"boolean","description":"Flag indicating if this period has no end date (for frequency=0 contingents with null toDate). When true, periodEnd represents the current date for display purposes, but the period is actually open-ended.","example":false},"tasks":{"description":"List of tasks billed or unbilled in this period","type":"array","items":{"$ref":"#/components/schemas/SupportContingentPeriodTaskDto"}}},"required":["periodStart","periodEnd","hours","hoursFromPrevious","consumedHours","isCurrentPeriod","tasks"]},"SupportContingentWithBreakdownResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the support contingent (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"projectId":{"type":"string","description":"ID of the project this contingent belongs to (UUID)","example":"550e8400-e29b-41d4-a716-446655440001"},"title":{"type":"string","description":"Title or name of the support contingent plan","example":"Maintenance flatrate"},"fromDate":{"type":"string","description":"Start date of the contingent period (ISO 8601 date format)","example":"2025-01-01"},"toDate":{"type":"string","description":"End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.","example":"2025-12-31"},"frequency":{"type":"number","description":"Frequency in months for the contingent period","example":12},"price":{"type":"number","description":"Total price for the contingent period","example":10000},"hours":{"type":"number","description":"Total hours allocated for the contingent period","example":120},"rate":{"type":"number","description":"Calculated hourly rate (price / hours). Automatically computed.","example":83.33},"transferable":{"type":"boolean","description":"Whether unused hours can be transferred to the next period","example":false},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the contingent was created","example":"2024-01-15T10:00:00Z"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp when the contingent was last updated","example":"2024-01-20T14:30:00Z"},"periods":{"description":"Period-by-period breakdown of the contingent, from start until present day","type":"array","items":{"$ref":"#/components/schemas/SupportContingentPeriodDto"}}},"required":["id","projectId","title","fromDate","frequency","price","hours","rate","transferable","createdAt","updatedAt","periods"]},"ObjectTypeListItemDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"icon":{"type":"object","nullable":true},"sort":{"type":"number"},"createdAt":{"type":"string"},"instanceCount":{"type":"number"},"statusCount":{"type":"number"}},"required":["id","name","slug","icon","sort","createdAt","instanceCount","statusCount"]},"CreateObjectStatusDto":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string","description":"Badge color (hex or token)"}},"required":["name","color"]},"UpdateObjectStatusDto":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"}},"required":["name","color"]},"CreateObjectTypeDto":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string","description":"URL slug; auto-generated from name if omitted"},"icon":{"type":"object","nullable":true}},"required":["name"]},"UpdateObjectTypeDto":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"icon":{"type":"object","nullable":true}},"required":["name"]},"ObjectTypeSummaryDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","nullable":true},"icon":{"type":"object","nullable":true}},"required":["id","name","slug","icon"]},"ObjectStatusSummaryDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"}},"required":["id","name","color"]},"OrganizationSummaryInObjectDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"shortName":{"type":"string"}},"required":["id","name","shortName"]},"ProjectSummaryInObjectDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"shortNumber":{"type":"number"}},"required":["id","name","shortNumber"]},"ObjectListItemDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"externalId":{"type":"object","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"editedAt":{"type":"string"},"objectType":{"$ref":"#/components/schemas/ObjectTypeSummaryDto"},"status":{"$ref":"#/components/schemas/ObjectStatusSummaryDto"},"organization":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/OrganizationSummaryInObjectDto"}]},"project":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ProjectSummaryInObjectDto"}]}},"required":["id","name","externalId","tags","createdAt","editedAt","objectType","status","organization","project"]},"PaginatedObjectsResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ObjectListItemDto"}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["items","total","page","pageSize"]},"ObjectFileCategorySummaryDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"ObjectFileResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"ObjectFile join row id (use for DELETE)"},"category":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ObjectFileCategorySummaryDto"}]},"description":{"type":"object","nullable":true},"createdAt":{"type":"string"},"uploadedBy":{"type":"string"},"file":{"type":"object"}},"required":["id","category","description","createdAt","uploadedBy","file"]},"AttachObjectFileDto":{"type":"object","properties":{"fileId":{"type":"string","description":"File id from POST /workspace/upload"},"categoryId":{"type":"object","nullable":true,"description":"Workspace object file category id"},"description":{"type":"object","nullable":true}},"required":["fileId"]},"AssignObjectsToOrganizationDto":{"type":"object","properties":{}},"UnassignObjectFromOrganizationDto":{"type":"object","properties":{}},"AssignObjectsToProjectDto":{"type":"object","properties":{}},"UnassignObjectFromProjectDto":{"type":"object","properties":{}},"ObjectJournalResponseDto":{"type":"object","properties":{"id":{"type":"string"},"objectId":{"type":"string"},"createdAt":{"type":"string"},"createdBy":{"type":"string"},"lastUpdated":{"type":"string"},"mood":{"type":"string","enum":["Sad","Neutral","Happy"]},"reminder":{"type":"object","nullable":true},"body":{"type":"string","description":"Body as HTML"}},"required":["id","objectId","createdAt","createdBy","lastUpdated","mood","reminder","body"]},"CreateObjectJournalDto":{"type":"object","properties":{"body":{"type":"string","description":"Journal entry body (HTML or Markdown). Converted to internal format for storage.","example":"<h1>Update</h1><p>Notes</p>"},"mood":{"type":"string","enum":["Sad","Neutral","Happy"]},"reminder":{"type":"string","example":"2024-12-31"}},"required":["body"]},"PatchObjectJournalDto":{"type":"object","properties":{"body":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n If omitted, body is unchanged."},"mood":{"type":"string","enum":["Sad","Neutral","Happy"]},"reminder":{"type":"object","nullable":true,"example":"2024-12-31"}}},"ObjectHistoryUserPublicDto":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"avatarId":{"type":"object","nullable":true}},"required":["id","firstName","lastName","avatarId"]},"ObjectHistoryFieldChangePublicDto":{"type":"object","properties":{"fieldKey":{"type":"string"},"label":{"type":"string"},"oldText":{"type":"string"},"newText":{"type":"string"},"oldStatusColor":{"type":"object","nullable":true,"description":"Hex color when fieldKey is statusId (old value)"},"newStatusColor":{"type":"object","nullable":true,"description":"Hex color when fieldKey is statusId (new value)"}},"required":["fieldKey","label","oldText","newText"]},"ObjectHistoryEntryPublicDto":{"type":"object","properties":{"id":{"type":"string"},"action":{"type":"string","enum":["Update","Create","Delete","Archive","Restore","Login","Logout"]},"createdAt":{"type":"string","description":"ISO 8601 timestamp"},"user":{"$ref":"#/components/schemas/ObjectHistoryUserPublicDto"},"changes":{"type":"array","items":{"$ref":"#/components/schemas/ObjectHistoryFieldChangePublicDto"}}},"required":["id","action","createdAt","user","changes"]},"PaginatedObjectHistoryResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ObjectHistoryEntryPublicDto"}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["items","total","page","pageSize"]},"TaskLinkedToObjectDto":{"type":"object","properties":{"id":{"type":"string"},"shortNumber":{"type":"number"},"title":{"type":"string"}},"required":["id","shortNumber","title"]},"TasksByObjectResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/TaskLinkedToObjectDto"}}},"required":["items"]},"LinkObjectToTaskPublicDto":{"type":"object","properties":{"objectId":{"type":"string","format":"uuid","description":"Object instance UUID"},"taskId":{"type":"string","format":"uuid","description":"Task UUID"}},"required":["objectId","taskId"]},"LinkObjectToTaskPublicResponseDto":{"type":"object","properties":{"objectId":{"type":"string","format":"uuid"},"taskId":{"type":"string","format":"uuid"}},"required":["objectId","taskId"]},"LinkObjectsBatchToTaskPublicDto":{"type":"object","properties":{"taskId":{"type":"string","format":"uuid","description":"Task UUID"},"objectIds":{"description":"Object instance UUIDs (1–200; duplicates are ignored server-side)","type":"array","items":{"type":"string"}}},"required":["taskId","objectIds"]},"LinkObjectsBatchPublicResponseDto":{"type":"object","properties":{"linked":{"type":"number","description":"Number of new links created (already-linked pairs are skipped)"}},"required":["linked"]},"ObjectInstanceResponseDto":{"type":"object","properties":{"id":{"type":"string"},"objectType":{"$ref":"#/components/schemas/ObjectTypeSummaryDto"},"status":{"$ref":"#/components/schemas/ObjectStatusSummaryDto"},"organizationId":{"type":"object","nullable":true},"organization":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/OrganizationSummaryInObjectDto"}]},"projectId":{"type":"object","nullable":true},"project":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ProjectSummaryInObjectDto"}]},"name":{"type":"string"},"externalId":{"type":"object","nullable":true},"customFields":{"type":"object"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"editedAt":{"type":"string"}},"required":["id","objectType","status","organizationId","organization","projectId","project","name","externalId","customFields","tags","createdAt","editedAt"]},"CreateObjectDto":{"type":"object","properties":{"objectTypeId":{"type":"string","description":"Object type UUID"},"organizationId":{"type":"object","nullable":true,"description":"Organization UUID; omit or null for internal object"},"projectId":{"type":"object","nullable":true,"description":"Assigned project UUID"},"statusId":{"type":"string","description":"Status UUID (must belong to the object type)"},"name":{"type":"string","description":"Display name"},"externalId":{"type":"object","nullable":true,"description":"External system identifier"},"customFields":{"type":"object","description":"Custom field values keyed by field id"},"tags":{"type":"array","items":{"type":"string"}}},"required":["objectTypeId","statusId","name"]},"UpdateObjectDto":{"type":"object","properties":{"organizationId":{"type":"object","nullable":true},"projectId":{"type":"object","nullable":true},"statusId":{"type":"string","description":"Status UUID (must belong to the object type)"},"name":{"type":"string"},"externalId":{"type":"object","nullable":true},"customFields":{"type":"object","description":"Custom field values keyed by field id"},"tags":{"type":"array","items":{"type":"string"}}}},"GetDocumentSettingsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Workspace ID","example":"123e4567-e89b-12d3-a456-426614174000"},"documentFont":{"type":"string","description":"Font family used for all documents generated by Leadtime. Available options: Inter (default), Degular, Roboto, Arimo, Open Sans, PT Sans, Oswald, EB Garamond, Permanent Marker, IBM Plex Mono.","example":"Inter","enum":["Inter","Degular","Roboto","Arimo","Open Sans","PT Sans","Oswald","EB Garamond","Permanent Marker","IBM Plex Mono"]},"documentLogoUrl":{"type":"object","description":"Public URL to the workspace logo image. This logo appears on all generated documents. If null, the Leadtime logo is used by default. The URL is publicly accessible without authentication.","example":"https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000","nullable":true},"projectDocumentEnableToc":{"type":"boolean","description":"Whether to automatically include a table of contents at the beginning of project documents (quotes, specifications, etc.). When enabled, a TOC is generated based on document headings.","example":true},"projectDocumentTitlePage":{"type":"boolean","description":"Whether to automatically include a title page in project documents. When enabled, a cover page is added as the first page with project and organization information.","example":true},"projectDocumentHeadingStyle":{"type":"string","description":"Controls how headings are numbered and formatted in project documents. Options: `Normal` (no numbering), `Sequential` (1., 1.1., 1.1.1.), `SequentialFromSecondLevel` (top heading unnumbered), `SequentialWithParagraphs` (§ 1., § 1.1. - ideal for contracts).","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential"},"specificationBackgroundUrl":{"type":"object","description":"Public URL to the background image used on specification document cover pages. Recommended size: 820 × 600 pixels. Formats: PNG, JPG, or SVG. The URL is publicly accessible without authentication.","example":"https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000","nullable":true},"estimateBackgroundUrl":{"type":"object","description":"Public URL to the background image used on estimate/proposal document cover pages. Recommended size: 820 × 600 pixels. Formats: PNG, JPG, or SVG. The URL is publicly accessible without authentication.","example":"https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000","nullable":true},"customEditorBackgroundUrl":{"type":"object","description":"Public URL to the background image used on custom editor document cover pages (documents created with the template system). Recommended size: 820 × 600 pixels. Formats: PNG, JPG, or SVG. The URL is publicly accessible without authentication.","example":"https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000","nullable":true},"documentLanguage":{"type":"string","description":"Default language code for project documents (e.g., \"en\", \"de\"). Used when document language is not specified.","example":"en"},"offerTexts":{"description":"Array of language-specific default offer texts. Each entry contains a language code and default intro/outro texts (returned as HTML).","type":"array","items":{"type":"object"}}},"required":["id","documentFont","documentLogoUrl","projectDocumentEnableToc","projectDocumentTitlePage","projectDocumentHeadingStyle","specificationBackgroundUrl","estimateBackgroundUrl","customEditorBackgroundUrl","documentLanguage","offerTexts"]},"PatchDocumentSettingsDto":{"type":"object","properties":{"documentFont":{"type":"string","description":"Font family to use for all documents. Must be one of: Inter, Degular, Roboto, Arimo, Open Sans, PT Sans, Oswald, EB Garamond, Permanent Marker, IBM Plex Mono. If not provided, current value is preserved.","example":"Inter","enum":["Inter","Degular","Roboto","Arimo","Open Sans","PT Sans","Oswald","EB Garamond","Permanent Marker","IBM Plex Mono"]},"documentLogoId":{"type":"object","description":"UUID of the logo image file uploaded via POST /api/public/workspace/upload. This logo appears on all generated documents. Set to `null` to remove the logo (defaults to Leadtime logo). If not provided, current value is preserved.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"projectDocumentEnableToc":{"type":"boolean","description":"Whether to automatically include a table of contents in project documents. When enabled, a TOC is generated based on document headings. If not provided, current value is preserved.","example":true},"projectDocumentTitlePage":{"type":"boolean","description":"Whether to automatically include a title page in project documents. When enabled, a cover page is added as the first page. If not provided, current value is preserved.","example":true},"projectDocumentHeadingStyle":{"type":"string","description":"Heading numbering style for project documents. Options: `Normal` (no numbering), `Sequential` (1., 1.1., 1.1.1.), `SequentialFromSecondLevel` (top heading unnumbered), `SequentialWithParagraphs` (§ 1., § 1.1.). If not provided, current value is preserved.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential"},"specificationBackgroundId":{"type":"object","description":"UUID of the specification background image file uploaded via POST /api/public/workspace/upload. Used on specification document cover pages. Recommended size: 820 × 600 pixels. Set to `null` to remove. If not provided, current value is preserved.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"estimateBackgroundId":{"type":"object","description":"UUID of the estimate/proposal background image file uploaded via POST /api/public/workspace/upload. Used on estimate document cover pages. Recommended size: 820 × 600 pixels. Set to `null` to remove. If not provided, current value is preserved.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"customEditorBackgroundId":{"type":"object","description":"UUID of the custom editor background image file uploaded via POST /api/public/workspace/upload. Used on template-based document cover pages. Recommended size: 820 × 600 pixels. Set to `null` to remove. If not provided, current value is preserved.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"documentLanguage":{"type":"string","description":"Default language code for project documents. Must be one of: en, de. If not provided, current value is preserved.","example":"en"},"offerTexts":{"description":"Array of language-specific offer texts. Each entry must include a language code and intro/outro texts (HTML or Markdown). If not provided, current values are preserved.","type":"array","items":{"type":"object"}}}},"EstimateGridItemResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the estimate. UUID format.","example":"123e4567-e89b-12d3-a456-426614174000"},"type":{"type":"string","description":"Type of estimate. PROJECT_DOCUMENT for detailed project estimates, EXPRESS_QUOTATION for quick ticket-based estimates.","enum":["PROJECT_DOCUMENT","EXPRESS_QUOTATION"],"example":"PROJECT_DOCUMENT"},"title":{"type":"string","description":"Human-readable title of the estimate. Used for quick search and display purposes.","example":"Website Redesign Estimate"},"organizationId":{"type":"string","description":"Unique identifier of the organization (customer) this estimate belongs to. UUID format.","example":"123e4567-e89b-12d3-a456-426614174001"},"organizationName":{"type":"string","description":"Display name of the organization. Provided for convenience to avoid additional lookups.","example":"Acme Corp"},"projectId":{"type":"string","description":"Unique identifier of the project this estimate is linked to. UUID format. Required for both estimate types.","example":"123e4567-e89b-12d3-a456-426614174002"},"projectName":{"type":"string","description":"Display name of the project. Provided for convenience to avoid additional lookups.","example":"Website Redesign"},"taskId":{"type":"string","description":"Unique identifier of the task (ticket) this express quotation is linked to. Only present for EXPRESS_QUOTATION type estimates. UUID format.","example":"123e4567-e89b-12d3-a456-426614174003"},"taskShortNumber":{"type":"number","description":"Short numeric identifier of the task. Only present for EXPRESS_QUOTATION type estimates. Used for display purposes (e.g., \"Task #42\").","example":42},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when the estimate was created. Format: YYYY-MM-DDTHH:mm:ss.sssZ","example":"2024-11-06T12:00:00.000Z"},"status":{"type":"string","description":"Current status of the estimate. Indicates where the estimate is in the sales process: Draft (being prepared), WaitingForApproval (pending internal approval), Final (approved), Pending (sent to customer), Accepted (customer accepted), Rejected (customer rejected), or Parked (temporarily set aside).","enum":["Draft","Final","WaitingForApproval","Pending","Accepted","Rejected","Parked"],"example":"Draft"},"total":{"type":"number","description":"Total price for express quotations. This is the sum of all price components (fixed + subscriptions + per-unit). Only present for EXPRESS_QUOTATION type estimates.","example":5000},"priceFixed":{"type":"number","description":"Fixed price component representing one-time charges for services or products. This is a single payment amount, not recurring.","example":3000},"priceSubscriptions":{"type":"number","description":"Subscription price component representing recurring charges (monthly, quarterly, semi-annually, or annually). The total of all subscription items in the estimate.","example":500},"pricePerUnit":{"type":"number","description":"Per-unit price component representing charges based on quantity (e.g., price per user license, price per workspace, price per API call). The total of all per-unit items in the estimate.","example":100},"priceSubscriptionsDetail":{"type":"object","description":"Detailed breakdown of subscription prices by product or service name. Key-value pairs where keys are product/service names and values are their subscription prices. Useful for understanding which items contribute to the total subscription price. Null if no subscription items exist.","example":{"hosting":200,"support":300}},"pricePerUnitDetail":{"type":"object","description":"Detailed breakdown of per-unit prices by product or service name. Key-value pairs where keys are product/service names and values are their per-unit prices. Useful for understanding which items contribute to the total per-unit price. Null if no per-unit items exist.","example":{"user-licenses":50,"api-calls":50}},"createdById":{"type":"string","description":"Unique identifier of the user who created this estimate. UUID format.","example":"123e4567-e89b-12d3-a456-426614174004"},"createdByName":{"type":"string","description":"Full name of the user who created this estimate. Provided for convenience to avoid additional user lookups.","example":"John Doe"}},"required":["id","type","title","organizationId","projectId","createdAt","status"]},"EstimateGridResponseDto":{"type":"object","properties":{"items":{"description":"Array of estimate items matching the query parameters. Each item represents either a project document estimate or an express quotation.","type":"array","items":{"$ref":"#/components/schemas/EstimateGridItemResponseDto"}},"total":{"type":"number","description":"Total number of estimate items matching the query filters across all pages. Use this value to calculate total pages: Math.ceil(total / pageSize).","example":150},"page":{"type":"number","description":"Current page number (1-based). Used for pagination navigation.","example":1},"pageSize":{"type":"number","description":"Number of items returned per page. Determines how many estimate items are included in the items array.","example":10},"totalPriceFixed":{"type":"number","description":"Aggregated sum of all fixed price components across all filtered estimate items. This represents the total one-time revenue potential from fixed-price items. Calculated by summing priceFixed from all items in the filtered result set.","example":125000},"totalPriceSubscriptions":{"type":"number","description":"Aggregated sum of all subscription price components across all filtered estimate items. This represents the total recurring revenue potential from subscription items. Calculated by summing priceSubscriptions from all items in the filtered result set.","example":45000},"totalPricePerUnit":{"type":"number","description":"Aggregated sum of all per-unit price components across all filtered estimate items. This represents the total revenue potential from per-unit items. Calculated by summing pricePerUnit from all items in the filtered result set.","example":15000}},"required":["items","total","page","pageSize","totalPriceFixed","totalPriceSubscriptions","totalPricePerUnit"]},"FeatureInfoDto":{"type":"object","properties":{"featureCode":{"type":"string","description":"Feature code identifier. Identifies which workspace feature this information applies to (e.g., customFields, advancedReports, apiAccess).","enum":["HELPDESK","API","AGENT"]},"label":{"type":"string","description":"Human-readable display label for the feature. Localized based on workspace language settings."},"enabled":{"type":"boolean","description":"Whether this feature is currently enabled and active for the workspace. Disabled features are not available for use."},"monthlyPrice":{"type":"object","description":"Monthly subscription price for this feature in cents. Multiply by 100 to get the display amount. Null if feature is not available on monthly billing.","nullable":true},"yearlyPrice":{"type":"object","description":"Yearly subscription price for this feature in cents. Multiply by 100 to get the display amount. Null if feature is not available on yearly billing.","nullable":true},"currency":{"type":"string","description":"ISO 4217 currency code (e.g., USD, EUR, GBP). All prices are in this currency."},"description":{"type":"string","description":"Optional description explaining what this feature provides and how it benefits the workspace."}},"required":["featureCode","label","enabled","monthlyPrice","yearlyPrice","currency"]},"SubscriptionItemDto":{"type":"object","properties":{"productName":{"type":"string","description":"Product name from Stripe. Identifies what is being billed (e.g., \"Leadtime Pro Plan\", \"Additional Seats\")."},"quantity":{"type":"number","description":"Quantity of this item in the subscription. For seat-based billing, this represents the number of seats."},"unitAmount":{"type":"number","description":"Price per unit in cents. Multiply by 100 to get the display amount. This is the price for a single unit before quantity multiplication."},"currency":{"type":"string","description":"ISO 4217 currency code (e.g., USD, EUR, GBP). All amounts for this item are in this currency."},"interval":{"type":"object","description":"Billing interval for this item. Values: \"month\" for monthly billing, \"year\" for yearly billing. Null if not applicable.","nullable":true},"totalAmount":{"type":"number","description":"Total amount for this line item in cents (unitAmount × quantity). Multiply by 100 to get the display amount."}},"required":["productName","quantity","unitAmount","currency","interval","totalAmount"]},"SubscriptionDetailsDto":{"type":"object","properties":{"status":{"type":"string","description":"Current subscription status from Stripe. Common values: \"active\" (subscription is active and paid), \"trialing\" (in trial period), \"past_due\" (payment failed), \"canceled\" (subscription canceled), \"unpaid\" (payment required)."},"currentPeriodStart":{"format":"date-time","type":"string","description":"Start date of the current billing period. ISO 8601 format. This is when the current billing cycle began."},"currentPeriodEnd":{"format":"date-time","type":"string","description":"End date of the current billing period. ISO 8601 format. This is when the current billing cycle ends and the next invoice will be generated."},"items":{"description":"List of all items included in this subscription. Each item represents a product or service being billed (e.g., base plan, additional seats, add-ons).","type":"array","items":{"$ref":"#/components/schemas/SubscriptionItemDto"}},"dashboardUrl":{"type":"string","description":"Direct URL to view and manage this subscription in the Stripe dashboard. Opens in a new window/tab. Only available for enterprise workspaces."}},"required":["status","currentPeriodStart","currentPeriodEnd","items","dashboardUrl"]},"TaxInfoDto":{"type":"object","properties":{"description":{"type":"string","description":"Human-readable description of the tax. Typically includes the tax type and jurisdiction (e.g., \"VAT\", \"Sales Tax\", \"GST\")."},"amount":{"type":"number","description":"Tax amount in cents. Multiply by 100 to get the display amount. This is the total tax for this specific tax line."},"percentage":{"type":"number","description":"Tax rate percentage (e.g., 19.0 for 19% VAT). Optional - may not be available for all tax types."}},"required":["description","amount"]},"InvoiceLineDto":{"type":"object","properties":{"description":{"type":"string","description":"Human-readable description of what this line item represents. Examples: \"Leadtime Pro Plan - Monthly\", \"Additional Seats (5)\", \"Setup Fee\"."},"quantity":{"type":"number","description":"Quantity of this item. For seat-based billing, this represents the number of seats. For fixed-price items, typically 1."},"unitAmount":{"type":"object","description":"Price per unit in cents before quantity multiplication. Multiply by 100 to get the display amount. Null if this is a fixed total amount line item.","nullable":true},"totalAmount":{"type":"number","description":"Total amount for this line item in cents (unitAmount × quantity, including any discounts). Multiply by 100 to get the display amount."},"amountExcludingTax":{"type":"number","description":"Total amount excluding tax in cents. Only present if tax is applied. Multiply by 100 to get the display amount."},"taxes":{"description":"Tax information specific to this line item. Multiple tax rates may apply to a single line item in some jurisdictions.","type":"array","items":{"$ref":"#/components/schemas/TaxInfoDto"}}},"required":["description","quantity","unitAmount","totalAmount"]},"TaxRateDto":{"type":"object","properties":{"displayName":{"type":"string","description":"Human-readable name for this tax rate. Examples: \"VAT\", \"Sales Tax\", \"GST\"."},"percentage":{"type":"number","description":"Tax rate as a percentage. Example: 19.0 represents 19% tax. Used to calculate tax amounts."}},"required":["displayName","percentage"]},"TaxLineDto":{"type":"object","properties":{"description":{"type":"string","description":"Human-readable description of the tax line. Typically includes tax type and jurisdiction."},"amount":{"type":"number","description":"Total tax amount for this tax line in cents. Multiply by 100 to get the display amount. This is the sum of all taxes at this rate."},"rate":{"description":"Detailed tax rate information including display name and percentage. Optional - may not be available for all tax types.","allOf":[{"$ref":"#/components/schemas/TaxRateDto"}]}},"required":["description","amount"]},"NextInvoiceDto":{"type":"object","properties":{"periodStart":{"type":"object","description":"Start date of the billing period this invoice covers. ISO 8601 format. Null if period information is not available.","nullable":true},"periodEnd":{"type":"object","description":"End date of the billing period this invoice covers. ISO 8601 format. Null if period information is not available.","nullable":true},"dueDate":{"type":"object","description":"Date when payment is due for this invoice. ISO 8601 format. Null if due date is not yet determined.","nullable":true},"lines":{"description":"All line items that will appear on the upcoming invoice. Includes base plan, seats, add-ons, and any other charges.","type":"array","items":{"$ref":"#/components/schemas/InvoiceLineDto"}},"subtotal":{"type":"number","description":"Subtotal amount in cents before taxes and discounts. Multiply by 100 to get the display amount. Sum of all line item totals."},"subtotal_excluding_tax":{"type":"number","description":"Subtotal excluding tax in cents. Only present if tax is applied. Multiply by 100 to get the display amount."},"taxLines":{"description":"All tax lines that will appear on the invoice. Each line represents a different tax rate or tax type.","type":"array","items":{"$ref":"#/components/schemas/TaxLineDto"}},"tax":{"type":"number","description":"Total tax amount in cents. Multiply by 100 to get the display amount. Sum of all tax line amounts."},"total":{"type":"number","description":"Final total amount due in cents including all line items and taxes. Multiply by 100 to get the display amount. This is the amount that will be charged."},"total_excluding_tax":{"type":"number","description":"Total amount excluding tax in cents. Only present if tax is applied. Multiply by 100 to get the display amount."},"currency":{"type":"string","description":"ISO 4217 currency code (e.g., USD, EUR, GBP). All amounts are in this currency."}},"required":["periodStart","periodEnd","dueDate","lines","subtotal","taxLines","tax","total","currency"]},"LatestInvoiceDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique Stripe invoice identifier. Use this to reference the invoice in Stripe API calls or support requests."},"number":{"type":"string","description":"Human-readable invoice number. This is the invoice number displayed to customers and used for accounting purposes."},"status":{"type":"string","description":"Current status of the invoice. Common values: \"paid\" (successfully paid), \"open\" (awaiting payment), \"void\" (canceled), \"uncollectible\" (payment failed)."},"amount":{"type":"number","description":"Total amount due for this invoice in cents. Multiply by 100 to get the display amount. This is the amount that was or will be charged."},"currency":{"type":"string","description":"ISO 4217 currency code (e.g., USD, EUR, GBP). All amounts for this invoice are in this currency."},"created":{"format":"date-time","type":"string","description":"Date and time when the invoice was created. ISO 8601 format."},"dueDate":{"type":"object","description":"Date when payment was or is due for this invoice. ISO 8601 format. Null if due date is not applicable.","nullable":true},"paidAt":{"type":"object","description":"Date and time when the invoice was successfully paid. ISO 8601 format. Null if invoice is not yet paid or payment failed.","nullable":true},"viewUrl":{"type":"string","description":"Direct URL to view this invoice in Stripe customer portal. Opens the invoice in a web browser. Use this to allow customers to view or download their invoices."}},"required":["id","number","status","amount","currency","created","dueDate","paidAt","viewUrl"]},"SubscriptionStatusResponseDto":{"type":"object","properties":{"billingMode":{"type":"string","description":"Workspace billing tier. Values: \"free\" (no paid subscription), \"regular\" (standard paid subscription), \"enterprise\" (custom enterprise agreement). Determines what features and billing details are available.","enum":["free","regular","enterprise"]},"billingStatus":{"type":"string","description":"Current subscription status. Values: \"active\" (subscription is active and paid), \"trialing\" (in trial period), \"past_due\" (payment failed), \"canceled\" (subscription canceled), \"unpaid\" (payment required). Only present for paid workspaces.","enum":["trialing","active","past_due","canceled"]},"planCode":{"type":"string","description":"Specific plan identifier. Identifies which subscription plan the workspace is on (e.g., \"pro\", \"team\", \"enterprise\"). Only present for paid workspaces.","enum":["regular"]},"billingInterval":{"type":"string","description":"Billing frequency. Values: \"monthly\" (billed every month), \"yearly\" (billed annually with potential discount). Only present for paid workspaces.","enum":["month","year"]},"features":{"type":"array","description":"List of workspace features currently enabled. Each feature code represents a capability or add-on that is active (e.g., customFields, advancedReports, apiAccess). Empty array for free workspaces.","items":{"type":"string","enum":["HELPDESK","API","AGENT"]}},"featuresInfo":{"description":"Detailed information about each enabled feature including pricing, labels, and descriptions. Useful for displaying feature lists with pricing in subscription management interfaces. Only present for paid workspaces.","type":"array","items":{"$ref":"#/components/schemas/FeatureInfoDto"}},"stripeCustomerId":{"type":"object","description":"Stripe customer identifier. Used to reference this workspace in Stripe API calls. Null for free workspaces or if Stripe integration is not set up.","nullable":true},"stripeSubscriptionId":{"type":"object","description":"Stripe subscription identifier. Used to reference the active subscription in Stripe API calls. Null for free workspaces or if no active subscription exists.","nullable":true},"trialEnd":{"type":"object","description":"End date of the trial period. ISO 8601 format. Null if workspace is not in trial or trial has ended. After this date, subscription will convert to paid or cancel.","nullable":true},"subscriptionDetails":{"description":"Detailed subscription information from Stripe including status, billing period, and subscription items. Only available for enterprise workspaces with active Stripe subscriptions. Null for free or regular workspaces.","nullable":true,"allOf":[{"$ref":"#/components/schemas/SubscriptionDetailsDto"}]},"nextInvoice":{"description":"Preview of the upcoming invoice that will be generated at the next billing cycle. Includes line items, taxes, and totals. Only available for paid workspaces with active subscriptions. Null for free workspaces or if preview cannot be generated.","nullable":true,"allOf":[{"$ref":"#/components/schemas/NextInvoiceDto"}]},"latestInvoices":{"description":"List of up to 5 most recent invoices. Includes invoice numbers, amounts, payment status, and direct links to view invoices. Only available for paid workspaces. Empty array for free workspaces or trial workspaces without payment history.","nullable":true,"type":"array","items":{"$ref":"#/components/schemas/LatestInvoiceDto"}},"billableUsersCount":{"type":"number","description":"Current number of billable users in the workspace. This is the count of users who are counted toward seat-based billing. Only present for paid workspaces."},"committedSeats":{"type":"number","description":"Number of seats currently committed and being billed for. This is the base seat count in the subscription. Only present for paid workspaces."},"pendingSeats":{"type":"object","description":"Number of additional seats scheduled to be added in the next billing period. Null if no seat changes are pending. Only present for paid workspaces.","nullable":true},"pendingEffectiveAt":{"type":"object","description":"Date when pending seat changes become effective. ISO 8601 format. Null if no seat changes are pending. Only present for paid workspaces.","nullable":true},"cancelAtPeriodEnd":{"type":"object","description":"Whether the subscription is scheduled to cancel at the end of the current billing period. True means subscription will cancel automatically, false means it will renew. Null if cancellation status is unknown.","nullable":true},"cancellationEffectiveAt":{"type":"object","description":"Date when the subscription cancellation becomes effective. ISO 8601 format. This is typically the end of the current billing period. Null if subscription is not scheduled to cancel.","nullable":true},"cancellationRequestedAt":{"type":"object","description":"Date when the cancellation was requested by the user. ISO 8601 format. Null if cancellation has not been requested.","nullable":true},"cancellationReason":{"type":"object","description":"Reason provided by the user when requesting cancellation. May include feedback about why they are canceling. Null if no reason was provided or cancellation was not user-initiated.","nullable":true},"nextBillingInterval":{"type":"string","description":"Billing interval that will take effect in the next billing cycle. Values: \"monthly\" or \"yearly\". Null if no interval change is scheduled. Only present when user has requested to switch between monthly and yearly billing.","enum":["month","year"],"nullable":true},"stillAvailableFeatures":{"type":"array","description":"List of features that will remain available after a billing interval change. Some features may be restricted when switching from yearly to monthly billing. Empty array if no interval change is scheduled or all features remain available.","items":{"type":"string","enum":["HELPDESK","API","AGENT"]}},"billingIntervalChangeScheduled":{"type":"object","description":"Date when a scheduled billing interval change will take effect. ISO 8601 format. Null if no interval change is scheduled.","nullable":true},"hasPaymentMethod":{"type":"boolean","description":"Whether the workspace has a valid payment method on file with Stripe. True means payment can be processed automatically, false means payment method needs to be added. Only present for paid workspaces."}},"required":["billingMode"]},"CreateInvoiceDto":{"type":"object","properties":{"potentialInvoiceId":{"type":"string","description":"Potential invoice identifier in the format: `{type}::{projectId}::{dateFrom}::{dateTo}`. The type can be one of: Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment. Dates must be in YYYY-MM-DD format. Example: `SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31`. To get available potential invoices, use the GET /billing/potential-invoices endpoint.","example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31"}},"required":["potentialInvoiceId"]},"InvoiceResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the created invoice","example":"123e4567-e89b-12d3-a456-426614174000"},"number":{"type":"string","description":"Invoice number in the format: `{ORG}{PRJ}-{YY}{MM}-{NNN}`. This is the official sequential invoice number used for accounting and customer communication.","example":"ACME01-2510-001"}},"required":["id","number"]},"InvoiceContactResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Contact ID (UUID) of the organization member. Use this ID when setting the contactId for a potential invoice via the PATCH endpoint.","format":"uuid"},"name":{"type":"string","description":"Full name of the contact (organization member). This is the person who will receive the invoice when it is sent via email.","example":"John Doe"},"avatarId":{"type":"object","description":"Avatar file ID (UUID) if the contact has a profile picture. Null if no avatar is set.","nullable":true,"format":"uuid"},"email":{"type":"object","description":"Email address of the contact. This is where the invoice will be sent when finalized. Null if the contact does not have an email address.","nullable":true,"example":"john.doe@example.com"}},"required":["id","name","avatarId","email"]},"InvoiceFixedSubscriptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Subscription billing ID","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Subscription title","example":"Monthly Support Package"},"amount":{"type":"number","description":"Subscription amount","example":500}},"required":["id","title","amount"]},"InvoiceVariableSubscriptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Subscription billing ID","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Subscription title","example":"Per-User License"},"amount":{"type":"number","description":"Total amount","example":1000},"quantity":{"type":"number","description":"Quantity (number of units)","example":10},"price":{"type":"number","description":"Price per unit","example":100}},"required":["id","title","amount","quantity","price"]},"InvoiceOneTimePaymentDto":{"type":"object","properties":{"id":{"type":"string","description":"One-time payment ID","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Payment title","example":"Setup Fee"},"amount":{"type":"number","description":"Total amount","example":500},"quantity":{"type":"number","description":"Quantity","example":1},"price":{"type":"number","description":"Price per unit","example":500}},"required":["id","title","amount","quantity","price"]},"InvoiceTaskDto":{"type":"object","properties":{"id":{"type":"string","description":"Task ID","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Task title","example":"Implement user authentication"},"spentTime":{"type":"number","description":"Time spent in hours","example":8.5},"estimatedTime":{"type":"number","description":"Estimated time in hours","example":8},"leadtimeFeeTime":{"type":"number","description":"Leadtime fee time in hours","example":0.5},"billedTime":{"type":"number","description":"Billed time in hours","example":8.5},"totalHours":{"type":"number","description":"Total hours (billed + leadtime fee)","example":9},"amount":{"type":"number","description":"Total amount","example":850},"taskTypeId":{"type":"string","description":"Task type ID","example":"123e4567-e89b-12d3-a456-426614174000"},"taskShortNumber":{"type":"number","description":"Task short number","example":123}},"required":["id","title","spentTime","estimatedTime","leadtimeFeeTime","billedTime","totalHours","amount","taskTypeId","taskShortNumber"]},"InvoiceExpressQuotationItemOptionDto":{"type":"object","properties":{"title":{"type":"string","description":"Option title","example":"Priority Support"},"unitPrice":{"type":"number","description":"Unit price","example":50},"totalPrice":{"type":"number","description":"Total price","example":50}},"required":["title","unitPrice","totalPrice"]},"InvoiceExpressQuotationItemDto":{"type":"object","properties":{"title":{"type":"string","description":"Item title","example":"Additional Development Hours"},"description":{"type":"string","description":"Item description as HTML","example":"<p>Extra development work</p>"},"unit":{"type":"string","description":"Unit type","example":"Hours"},"quantity":{"type":"number","description":"Quantity","example":5},"unitPrice":{"type":"number","description":"Unit price","example":100},"totalPrice":{"type":"number","description":"Total price","example":500},"order":{"type":"number","description":"Order","example":1},"options":{"description":"Item options","type":"array","items":{"$ref":"#/components/schemas/InvoiceExpressQuotationItemOptionDto"}}},"required":["title","description","unit","quantity","unitPrice","totalPrice","order","options"]},"InvoiceExpressQuotationDto":{"type":"object","properties":{"id":{"type":"string","description":"Express quotation ID","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Quotation title","example":"Quick Add-on Development"},"quotationId":{"type":"string","description":"Quotation ID","example":"123e4567-e89b-12d3-a456-426614174000"},"amount":{"type":"number","description":"Total amount","example":500},"acceptedAt":{"type":"string","description":"Accepted date","example":"2025-01-15T10:00:00Z"},"acceptedByName":{"type":"string","description":"Accepted by user name","example":"John Doe"},"taskShortNumber":{"type":"number","description":"Task short number","example":123},"quotationShortNumber":{"type":"number","description":"Quotation short number","example":456},"fullNumber":{"type":"string","description":"Full quotation number","example":"EQ-456"},"taskId":{"type":"string","description":"Task ID","example":"123e4567-e89b-12d3-a456-426614174000"},"items":{"description":"Quotation items","type":"array","items":{"$ref":"#/components/schemas/InvoiceExpressQuotationItemDto"}}},"required":["id","title","quotationId","amount","acceptedAt","acceptedByName","taskShortNumber","quotationShortNumber","fullNumber","taskId","items"]},"InvoiceManualPositionDto":{"type":"object","properties":{"id":{"type":"string","description":"Manual position ID","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Position title","example":"Third-party API License"},"amount":{"type":"number","description":"Total amount","example":200},"type":{"type":"string","description":"Manual position type","enum":["manual","project","interimPayment","interimPaymentDiscount"]},"projectManualPositionId":{"type":"object","description":"Project manual position ID","example":"123e4567-e89b-12d3-a456-426614174000"},"discountAmount":{"type":"number","description":"Discount amount","example":0},"discountType":{"type":"string","description":"Discount type","enum":["Fixed","Percentage"]},"discountValue":{"type":"number","description":"Discount value","example":0},"categoryName":{"type":"object","description":"Category name","example":"Licenses"},"description":{"type":"object","description":"Description as HTML","example":"<p>Annual license fee</p>"},"interimPaymentId":{"type":"object","description":"Interim payment ID","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["id","title","amount"]},"InvoiceProductOptionValueDto":{"type":"object","properties":{"id":{"type":"string","description":"Option value ID","example":"123e4567-e89b-12d3-a456-426614174000"},"originalValueId":{"type":"object","description":"Original option value ID from catalog","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Option value name","example":"Large"},"extraPriceFixed":{"type":"object","description":"Extra price for this option value","example":50},"sort":{"type":"number","description":"Sort order","example":1}},"required":["id","name","sort"]},"InvoiceProductOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Option ID","example":"123e4567-e89b-12d3-a456-426614174000"},"originalOptionId":{"type":"object","description":"Original option ID from catalog","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Option name","example":"Size"},"sort":{"type":"number","description":"Sort order","example":1},"values":{"description":"Available option values","type":"array","items":{"$ref":"#/components/schemas/InvoiceProductOptionValueDto"}}},"required":["id","name","sort","values"]},"InvoiceProductDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice product ID","example":"123e4567-e89b-12d3-a456-426614174000"},"originalProductId":{"type":"object","description":"Original product ID from catalog","example":"123e4567-e89b-12d3-a456-426614174000"},"categoryId":{"type":"string","description":"Product category ID","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Product name","example":"Website Development Package"},"description":{"type":"object","description":"Product description as HTML","example":"<p>Complete website development package</p>"},"logoId":{"type":"object","description":"Product logo file ID","example":"123e4567-e89b-12d3-a456-426614174000"},"sort":{"type":"number","description":"Sort order","example":1},"basePriceFixed":{"type":"number","description":"Base price before discounts","example":1000},"priceFixed":{"type":"number","description":"Price after discounts","example":900},"fixedDiscountAmount":{"type":"number","description":"Discount amount","example":100},"fixedDiscountType":{"type":"string","description":"Discount type","enum":["Fixed","Percentage"],"example":"Percentage"},"fixedDiscountValue":{"type":"number","description":"Discount value","example":10},"quantity":{"type":"number","description":"Quantity","example":1},"totalPrice":{"type":"number","description":"Total price (priceFixed * quantity)","example":900},"options":{"description":"Product options","type":"array","items":{"$ref":"#/components/schemas/InvoiceProductOptionDto"}}},"required":["id","categoryId","name","sort","basePriceFixed","priceFixed","fixedDiscountAmount","fixedDiscountType","fixedDiscountValue","quantity","totalPrice","options"]},"InvoiceComponentItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Component item ID","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Item name","example":"User Authentication"},"description":{"type":"string","description":"Item description as HTML","example":"<p>Implement user authentication system</p>"},"type":{"type":"string","description":"Item type","enum":["Epic","WorkPackage","TodoList","TestSuite"],"example":"WorkPackage"},"icon":{"type":"object","description":"Item icon","example":"🔐"},"sort":{"type":"number","description":"Sort order","example":1},"timeFrame":{"type":"object","description":"Time frame in hours","example":40},"totalEstimate":{"type":"object","description":"Total estimate in hours","example":40},"hourRate":{"type":"number","description":"Hourly rate","example":100},"amount":{"type":"number","description":"Total amount","example":4000},"parentId":{"type":"object","description":"Parent item ID","example":"123e4567-e89b-12d3-a456-426614174000"},"children":{"description":"Nested child items","type":"array","items":{"$ref":"#/components/schemas/InvoiceComponentItemDto"}},"originalItemId":{"type":"object","description":"Original item ID from project component","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["id","name","description","type","sort","hourRate","amount"]},"InvoiceComponentDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice component ID","example":"123e4567-e89b-12d3-a456-426614174000"},"originalComponentId":{"type":"object","description":"Original component ID from project","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Component name","example":"Backend Development"},"description":{"type":"string","description":"Component description as HTML","example":"<p>Backend API development</p>"},"icon":{"type":"object","description":"Component icon","example":"⚙️"},"sort":{"type":"number","description":"Sort order","example":1},"totalTimeFrame":{"type":"number","description":"Total time frame in hours","example":120},"hourRate":{"type":"number","description":"Hourly rate","example":100},"amount":{"type":"number","description":"Total amount after discount","example":12000},"amountWithoutDiscount":{"type":"number","description":"Amount before discount","example":13000},"discountAmount":{"type":"number","description":"Discount amount","example":1000},"discountType":{"type":"string","description":"Discount type","enum":["Fixed","Percentage"],"example":"Percentage"},"items":{"description":"Component items","type":"array","items":{"$ref":"#/components/schemas/InvoiceComponentItemDto"}}},"required":["id","name","description","sort","totalTimeFrame","hourRate","amount","amountWithoutDiscount","discountAmount","discountType","items"]},"InvoiceSupportContingentChargeDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice support contingent charge ID","example":"123e4567-e89b-12d3-a456-426614174000"},"supportContingentId":{"type":"string","description":"Support contingent ID (UUID) that this charge is based on","example":"123e4567-e89b-12d3-a456-426614174000"},"title":{"type":"string","description":"Title of the support contingent","example":"Monthly Support Retainer"},"periodStart":{"type":"string","description":"Billing period start date (ISO 8601 date string, YYYY-MM-DD format)","example":"2025-08-01"},"periodEnd":{"type":"string","description":"Billing period end date (ISO 8601 date string, YYYY-MM-DD format)","example":"2025-08-31"},"frequency":{"type":"number","description":"Billing frequency in months. Determines how often this contingent is charged (e.g., 1 = monthly, 3 = quarterly)","example":1},"hoursIncluded":{"type":"number","description":"Number of hours included in this billing period","example":50},"price":{"type":"number","description":"Total price for this billing period (in organization currency)","example":500},"hourRate":{"type":"number","description":"Hourly rate used for calculating the price (in organization currency)","example":10},"transferable":{"type":"boolean","description":"Whether unused hours from this period can be transferred to future periods","example":true}},"required":["id","supportContingentId","title","periodStart","periodEnd","frequency","hoursIncluded","price","hourRate","transferable"]},"InvoiceDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice ID (UUID)","example":"123e4567-e89b-12d3-a456-426614174000"},"number":{"type":"string","description":"Invoice number","example":"ACME01-2510-001"},"type":{"type":"string","description":"Invoice type","enum":["Mixed","Subscription","TimeBased","ExpressQuotation","SingleProject","InterimPayment","SupportContingentCharge"],"example":"Mixed"},"includedTypes":{"type":"array","description":"Included invoice types","example":["Mixed"],"items":{"type":"number","enum":[{"Mixed":"Mixed","Subscription":"Subscription","TimeBased":"TimeBased","ExpressQuotation":"ExpressQuotation","SingleProject":"SingleProject","InterimPayment":"InterimPayment","SupportContingentCharge":"SupportContingentCharge"}]}},"projectId":{"type":"string","description":"Project ID","example":"123e4567-e89b-12d3-a456-426614174000"},"createdAt":{"type":"string","description":"Invoice creation date","example":"2025-01-15T10:00:00Z"},"billedFrom":{"type":"string","description":"Billing period start date","example":"2025-01-01"},"billedTo":{"type":"string","description":"Billing period end date","example":"2025-01-31"},"status":{"type":"string","description":"Invoice status","enum":["New","Sent","Paid","Cancelled"],"example":"New"},"statusChangedAt":{"type":"object","description":"Status change date","example":"2025-01-15T10:00:00Z"},"isOverdue":{"type":"boolean","description":"Whether invoice is overdue","example":false},"overDueDays":{"type":"number","description":"Number of days overdue","example":0},"dueDate":{"type":"object","description":"Payment due date","example":"2025-02-15"},"paidAt":{"type":"object","description":"Payment received date","example":"2025-01-20T10:00:00Z"},"paidRuntime":{"type":"object","description":"Payment runtime in days","example":5},"sentAt":{"type":"object","description":"Invoice sent date","example":"2025-01-15T10:00:00Z"},"sentBy":{"type":"object","description":"User ID who sent the invoice","example":"123e4567-e89b-12d3-a456-426614174000"},"sentType":{"type":"string","description":"How invoice was sent","enum":["Manual","Email"],"example":"Email"},"sentToContactId":{"type":"object","description":"Contact ID who received the invoice","example":"123e4567-e89b-12d3-a456-426614174000"},"firstReminderAt":{"type":"object","description":"First reminder sent date","example":"2025-02-20T10:00:00Z"},"firstReminderContactId":{"type":"object","description":"First reminder contact ID","example":"123e4567-e89b-12d3-a456-426614174000"},"firstReminderType":{"type":"string","description":"How first reminder was sent","enum":["Manual","Email"],"example":"Email"},"firstReminderSentBy":{"type":"object","description":"User ID who sent first reminder","example":"123e4567-e89b-12d3-a456-426614174000"},"secondReminderAt":{"type":"object","description":"Second reminder sent date","example":"2025-03-01T10:00:00Z"},"secondReminderContactId":{"type":"object","description":"Second reminder contact ID","example":"123e4567-e89b-12d3-a456-426614174000"},"secondReminderType":{"type":"string","description":"How second reminder was sent","enum":["Manual","Email"],"example":"Email"},"secondReminderSentBy":{"type":"object","description":"User ID who sent second reminder","example":"123e4567-e89b-12d3-a456-426614174000"},"contactId":{"type":"object","description":"Invoice contact ID","example":"123e4567-e89b-12d3-a456-426614174000"},"cancelationSentAt":{"type":"object","description":"Cancellation sent date","example":"2025-01-20T10:00:00Z"},"cancelationSentContactId":{"type":"object","description":"Cancellation contact ID","example":"123e4567-e89b-12d3-a456-426614174000"},"cancelationSentType":{"type":"string","description":"How cancellation was sent","enum":["Manual","Email"],"example":"Email"},"cancelationSentBy":{"type":"object","description":"User ID who sent cancellation","example":"123e4567-e89b-12d3-a456-426614174000"},"hourRate":{"type":"number","description":"Hourly rate used for time-based billing","example":100},"fixedSubscriptions":{"description":"Fixed subscriptions","type":"array","items":{"$ref":"#/components/schemas/InvoiceFixedSubscriptionDto"}},"variableSubscriptions":{"description":"Variable subscriptions","type":"array","items":{"$ref":"#/components/schemas/InvoiceVariableSubscriptionDto"}},"oneTimePayments":{"description":"One-time payments","type":"array","items":{"$ref":"#/components/schemas/InvoiceOneTimePaymentDto"}},"tasksCount":{"type":"number","description":"Number of tasks billed","example":10},"tasksHoursBilled":{"type":"number","description":"Total hours billed from tasks","example":80.5},"tasksSum":{"type":"number","description":"Total amount from tasks","example":8050},"tasks":{"description":"Billed tasks","type":"array","items":{"$ref":"#/components/schemas/InvoiceTaskDto"}},"expressQuotations":{"description":"Express quotations","type":"array","items":{"$ref":"#/components/schemas/InvoiceExpressQuotationDto"}},"manualRows":{"description":"Manual billing positions","type":"array","items":{"$ref":"#/components/schemas/InvoiceManualPositionDto"}},"invoiceProducts":{"description":"Invoice products","type":"array","items":{"$ref":"#/components/schemas/InvoiceProductDto"}},"invoiceProductsCount":{"type":"number","description":"Number of products billed","example":5},"invoiceProductsSum":{"type":"number","description":"Total amount from products","example":5000},"invoiceComponents":{"description":"Invoice components","type":"array","items":{"$ref":"#/components/schemas/InvoiceComponentDto"}},"componentsCount":{"type":"number","description":"Number of components billed","example":3},"componentsSum":{"type":"number","description":"Total amount from components","example":12000},"supportContingentCharges":{"description":"Support contingent charges billed on this invoice","type":"array","items":{"$ref":"#/components/schemas/InvoiceSupportContingentChargeDto"}},"supportContingentChargesCount":{"type":"number","description":"Number of support contingent charges billed","example":1},"supportContingentChargesSum":{"type":"number","description":"Total amount from support contingent charges","example":500},"leadtimeFeeIncluded":{"type":"boolean","description":"Whether Leadtime fee is included","example":true},"leadtimeFeePercent":{"type":"number","description":"Leadtime fee percentage","example":10},"leadtimeFeeMethod":{"type":"string","description":"Leadtime fee calculation method","enum":["SeparateRow","TasksDistribution"],"example":"TasksDistribution"},"leadtimeRowCustomTitle":{"type":"object","description":"Custom title for Leadtime fee row","example":"Service Fee"},"leadtimeFee":{"type":"number","description":"Leadtime fee amount","example":500},"netto":{"type":"number","description":"Net amount (before taxes and fees)","example":10000},"brutto":{"type":"number","description":"Gross amount (after taxes)","example":11900},"reverseVat":{"type":"boolean","description":"Whether reverse VAT charge applies","example":false},"vatAmount":{"type":"number","description":"VAT amount","example":1900},"vat":{"type":"number","description":"VAT percentage","example":19},"fullTotal":{"type":"number","description":"Full total including all fees","example":12400},"potentialOverdueReminderFee":{"type":"number","description":"Potential overdue reminder fee","example":0},"potentialOverdueInterest":{"type":"number","description":"Potential overdue interest","example":0},"overdueReminderFee":{"type":"number","description":"Overdue reminder fee","example":0},"overdueInterestPercent":{"type":"number","description":"Overdue interest percentage","example":0},"overdueInterestDays":{"type":"number","description":"Overdue interest days","example":0},"overdueInterest":{"type":"number","description":"Overdue interest amount","example":0}},"required":["id","number","type","includedTypes","projectId","createdAt","billedFrom","billedTo","status","isOverdue","overDueDays","sentType","firstReminderType","secondReminderType","cancelationSentType","hourRate","fixedSubscriptions","variableSubscriptions","oneTimePayments","tasksCount","tasksHoursBilled","tasksSum","tasks","expressQuotations","manualRows","invoiceProducts","invoiceProductsCount","invoiceProductsSum","invoiceComponents","componentsCount","componentsSum","supportContingentCharges","supportContingentChargesCount","supportContingentChargesSum","leadtimeFeeIncluded","leadtimeFeePercent","leadtimeFeeMethod","leadtimeFee","netto","brutto","reverseVat","vatAmount","vat","fullTotal","potentialOverdueReminderFee","potentialOverdueInterest","overdueReminderFee","overdueInterestPercent","overdueInterestDays","overdueInterest"]},"SetInvoiceContactIdDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice ID (UUID) to update the contact for","example":"123e4567-e89b-12d3-a456-426614174000","format":"uuid"},"contactId":{"type":"object","description":"Contact ID (UUID) of the organization member who should receive the invoice. This must be a valid contact from the project's customer organization. Set to null to clear the contact. Use GET /billing/invoiceContacts/{projectId} to get available contacts.","example":"123e4567-e89b-12d3-a456-426614174000","format":"uuid","nullable":true}},"required":["id"]},"SendInvoiceDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice ID (UUID) to send. The invoice must have a contact with a valid email address set. The invoice will be sent via email as a PDF attachment.","example":"123e4567-e89b-12d3-a456-426614174000","format":"uuid"}},"required":["id"]},"InvoiceSettingsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Invoice ID","example":"uuid-123"},"invoiceLanguage":{"type":"object","description":"Invoice language","example":"en","nullable":true},"enableInvoiceReminderFee":{"type":"object","description":"Enable invoice reminder fee","example":true,"nullable":true},"invoiceReminderFee":{"type":"object","description":"Invoice reminder fee","example":5.5,"nullable":true},"enableInvoiceInterest":{"type":"object","description":"Enable invoice interest","example":true,"nullable":true},"baseInvoiceInterest":{"type":"object","description":"Base invoice interest","example":2.5,"nullable":true},"invoiceInterest":{"type":"object","description":"Invoice interest","example":8,"nullable":true},"parentSettings":{"type":"object","description":"Parent settings (from organization or workspace)","additionalProperties":true,"example":{"invoiceLanguage":"en","enableInvoiceReminderFee":true,"invoiceReminderFee":5.5,"enableInvoiceInterest":true,"invoiceInterest":8,"baseInvoiceInterest":2.5}},"customTexts":{"type":"object","description":"Custom invoice text templates (returned as HTML)","properties":{"withBestRegards":{"type":"string","nullable":true},"invoiceGreeting":{"type":"string","nullable":true},"invoiceFooter":{"type":"string","nullable":true},"invoiceReminderFeeWarning":{"type":"string","nullable":true},"firstReminderGreeting":{"type":"string","nullable":true},"firstReminderFooter":{"type":"string","nullable":true},"secondReminderGreeting":{"type":"string","nullable":true},"secondReminderFooter":{"type":"string","nullable":true},"cancelationBody":{"type":"string","nullable":true}}}},"required":["id","invoiceLanguage","enableInvoiceReminderFee","invoiceReminderFee","enableInvoiceInterest","baseInvoiceInterest","invoiceInterest","parentSettings","customTexts"]},"DefaultInvoiceTextsResponseDto":{"type":"object","properties":{"withBestRegards":{"type":"object","description":"Default invoice text templates (returned as HTML)","properties":{"withBestRegards":{"type":"string"},"invoiceGreeting":{"type":"string"},"invoiceFooter":{"type":"string"},"invoiceReminderFeeWarning":{"type":"string"},"firstReminderGreeting":{"type":"string"},"firstReminderFooter":{"type":"string"},"secondReminderGreeting":{"type":"string"},"secondReminderFooter":{"type":"string"},"cancelationBody":{"type":"string"}}}},"required":["withBestRegards"]},"InvoiceCustomTextsDto":{"type":"object","properties":{"withBestRegards":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>Best regards,<br>Your Team</p>","nullable":true},"invoiceGreeting":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>Dear Customer</p>","nullable":true},"invoiceFooter":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>Thank you for your business</p>","nullable":true},"invoiceReminderFeeWarning":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>A reminder fee will be applied if payment is not received</p>","nullable":true},"firstReminderGreeting":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>First reminder for invoice payment</p>","nullable":true},"firstReminderFooter":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>Please pay within 14 days</p>","nullable":true},"secondReminderGreeting":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>Second reminder for invoice payment</p>","nullable":true},"secondReminderFooter":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>Final notice before legal action</p>","nullable":true},"cancelationBody":{"type":"object","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your invoice text templates. Variables are represented as HTML spans:\n\n`invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"invoiceNumber\">INV-2024-001</span>`, `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`","example":"<p>This invoice has been cancelled</p>","nullable":true}}},"PatchInvoiceSettingsDto":{"type":"object","properties":{"invoiceDueDays":{"type":"object","description":"Number of days until invoice is due (1-120)","example":30,"nullable":true},"enableInvoiceReminderFee":{"type":"object","description":"Enable invoice reminder fee (null = use default)","example":true,"nullable":true},"invoiceReminderFee":{"type":"object","description":"Invoice reminder fee amount (0-1000)","example":5.5,"nullable":true},"enableInvoiceInterest":{"type":"object","description":"Enable invoice interest (null = use default)","example":true,"nullable":true},"baseInvoiceInterest":{"type":"object","description":"Base invoice interest rate percentage (0-1000)","example":2.5,"nullable":true},"invoiceInterest":{"type":"object","description":"Invoice interest rate percentage (0-1000)","example":8,"nullable":true},"invoiceLanguage":{"type":"object","description":"Invoice language code","example":"en","nullable":true},"customTexts":{"description":"Custom invoice text templates. Content can be provided as HTML or Markdown and will be converted to internal format.","allOf":[{"$ref":"#/components/schemas/InvoiceCustomTextsDto"}]}}},"InvoiceFileUrlResponseDto":{"type":"object","properties":{"url":{"type":"string","description":"Public URL to access the generated invoice file","example":"https://app.example.com/api/files/public/file_123"},"filename":{"type":"string","description":"Filename of the generated invoice file","example":"invoice-ACME01-2510-001.pdf"}},"required":["url","filename"]},"PotentialInvoiceResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the potential invoice. Format: {type}::{projectId}::{dateFrom}::{dateTo}. This ID is used to reference the invoice in other endpoints.","example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31","format":"string"},"projectId":{"type":"string","description":"Project ID (UUID) associated with this potential invoice. This is the project where the billable work was performed.","example":"550e8400-e29b-41d4-a716-446655440000","format":"uuid"},"orgId":{"type":"string","description":"Organization ID (UUID) of the customer organization for this potential invoice. This is the organization that will be billed.","example":"660e8400-e29b-41d4-a716-446655440001","format":"uuid"},"dateKey":{"type":"string","description":"Date key used for grouping potential invoices by month (format: YYYY-MM). Used for organizing invoices in the list view.","example":"2024-01"},"dateFrom":{"type":"string","description":"Start date of the billing period (ISO 8601 date string, YYYY-MM-DD format). This is when the billable period begins.","example":"2024-01-01","format":"date"},"dateTo":{"type":"string","description":"End date of the billing period (ISO 8601 date string, YYYY-MM-DD format). This is when the billable period ends.","example":"2024-01-31","format":"date"},"total":{"type":"number","description":"Total net amount for this potential invoice (before tax). This is the sum of all billable items in the invoice.","example":1250.5},"type":{"type":"string","description":"Type of invoice indicating what kind of billable items are included. See InvoiceType enum for all possible values.","enum":["Mixed","Subscription","TimeBased","ExpressQuotation","SingleProject","InterimPayment","SupportContingentCharge"],"example":"Mixed"}},"required":["id","projectId","orgId","dateKey","dateFrom","dateTo","total","type"]},"PotentialInvoiceDetailsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the potential invoice. Format: {type}::{projectId}::{dateFrom}::{dateTo}","example":"SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31"},"hasMeta":{"type":"boolean","description":"Whether this potential invoice has custom metadata or settings applied. If true, the invoice has been customized (custom dates, contact, skipped items, etc.). If false, it shows the default calculated values.","example":true},"periodFrom":{"type":"string","description":"Actual service period start date (ISO 8601 date string, YYYY-MM-DD format). This is when the billable work actually began.","example":"2025-10-01"},"periodTo":{"type":"string","description":"Actual service period end date (ISO 8601 date string, YYYY-MM-DD format). This is when the billable work actually ended.","example":"2025-10-31"},"type":{"type":"string","description":"Primary invoice type indicating the main category of billable items. See InvoiceType enum for all possible values.","enum":["Mixed","Subscription","TimeBased","ExpressQuotation","SingleProject","InterimPayment","SupportContingentCharge"]},"includedTypes":{"type":"array","description":"Array of all invoice types included in this potential invoice. For Mixed type invoices, this will contain multiple types (e.g., TimeBased, Subscription, Products).","items":{"type":"string","enum":["Mixed","Subscription","TimeBased","ExpressQuotation","SingleProject","InterimPayment","SupportContingentCharge"]}},"projectId":{"type":"string","description":"Project ID (UUID) where the billable work was performed","format":"uuid"},"organizationId":{"type":"string","description":"Organization ID (UUID) of the customer organization that will be billed","format":"uuid"},"orgHourlyRate":{"type":"number","description":"Hourly rate (in organization currency) used for calculating time-based billing. This rate is applied to logged hours on tasks."},"tax":{"type":"number","description":"Tax amount calculated for this invoice. The tax rate is configured in workspace settings."},"applyReverseCharge":{"type":"boolean","description":"Whether reverse charge VAT is applied. Reverse charge is used for B2B transactions within the EU where the customer is responsible for VAT.","example":false},"contactId":{"type":"object","description":"Contact ID (UUID) of the organization member who will receive the invoice when it is sent via email. Null if no contact has been assigned yet.","nullable":true,"format":"uuid"},"billedFrom":{"type":"string","description":"Custom billed from date (ISO 8601 date string, YYYY-MM-DD format). This is the start date that will appear on the final invoice document. Can differ from periodFrom if you want to show a different date range. Null if using default periodFrom.","nullable":true},"billedTo":{"type":"string","description":"Custom billed to date (ISO 8601 date string, YYYY-MM-DD format). This is the end date that will appear on the final invoice document. Can differ from periodTo if you want to show a different date range. Null if using default periodTo.","nullable":true},"leadtimeFeePercent":{"type":"number","description":"Leadtime service fee percentage. This is the percentage of the invoice total that will be charged as the Leadtime platform fee."},"addLeadtimeFeeToInvoice":{"type":"boolean","description":"Whether to add the Leadtime service fee as a separate line item on the invoice. If false, the fee is not shown on the invoice but may still be calculated separately.","example":false},"addLeadtimeFeeMethod":{"type":"string","description":"Method for how the Leadtime fee should be added to the invoice. See AddLeadtimeFeeMethod enum for options (e.g., SeparateRow, DistributedToTasks).","enum":["SeparateRow","TasksDistribution"]},"addLeadtimeFeeTasks":{"description":"Array of task IDs (UUIDs) for distributing the Leadtime fee across specific tasks. Used when addLeadtimeFeeMethod is set to distribute the fee to tasks.","type":"array","items":{"type":"string"}},"addLeadtimeRowCustomTitle":{"type":"object","description":"Custom title for the Leadtime fee row on the invoice. If null, a default title is used. This allows you to customize how the fee appears on the invoice document.","nullable":true},"subscriptions":{"description":"Array of subscription billing rows. These are recurring fees (monthly or periodic) that are due for billing. Each row includes billing type, payment type, price, quantity, and optional manual title.","items":{"type":"array"},"type":"array"},"expressQuotations":{"description":"Array of accepted express quotations. Express quotations are quick quotes created directly from tickets that have been accepted by the customer. Each quotation includes items, quantities, unit prices, and acceptance details.","items":{"type":"array"},"type":"array"},"tasks":{"description":"Array of time-based tasks (tickets) with logged hours. These are completed tasks that have time logged and are ready for billing. Each task includes logged time, estimated time, billed time, and billing information.","items":{"type":"array"},"type":"array"},"products":{"description":"Array of products from the product catalog. These are standardized products or services with fixed prices, quantities, options, and discounts. Product descriptions are returned as HTML.","items":{"type":"array"},"type":"array"},"components":{"description":"Array of project components with time estimates and hourly rates. Components are work packages from the project structure. Each component includes nested items, time frames, hourly rates, and amounts. Descriptions are returned as HTML.","items":{"type":"array"},"type":"array"},"manualPositions":{"description":"Array of manual positions (custom billing items). These are manually added items like third-party costs, flat fees, interim payments, or other custom charges. Can include project manual positions or custom positions added during invoice review.","items":{"type":"array"},"type":"array"},"supportContingentCharges":{"description":"Array of support contingent charges. These are retainer plan charges for Support-type projects, based on allocated hours and pricing for billing periods. Each charge includes the contingent title, period (start/end dates), frequency, hours included, price, hourly rate, and transferable flag.","items":{"type":"array"},"type":"array"},"skippedTasks":{"description":"Array of task IDs (UUIDs) that have been excluded from billing. These are tasks that would normally be billable but have been skipped (marked as \"do not bill\").","type":"array","items":{"type":"string"}},"skippedExpressQuotationTasks":{"description":"Array of express quotation task IDs (UUIDs) that have been excluded from billing. These are accepted express quotations that have been skipped and will not appear on the invoice.","type":"array","items":{"type":"string"}}},"required":["id","hasMeta","periodFrom","periodTo","type","includedTypes","projectId","organizationId","orgHourlyRate","tax","applyReverseCharge","contactId","billedFrom","billedTo","leadtimeFeePercent","addLeadtimeFeeToInvoice","addLeadtimeFeeMethod","addLeadtimeFeeTasks","addLeadtimeRowCustomTitle","subscriptions","expressQuotations","tasks","products","components","manualPositions","supportContingentCharges","skippedTasks","skippedExpressQuotationTasks"]},"PatchPotentialInvoiceDto":{"type":"object","properties":{"billedFrom":{"type":"string","description":"Billed from date (ISO 8601 date string, YYYY-MM-DD format). If provided, updates the start date that will appear on the final invoice document. This can differ from the actual service period start date (periodFrom). Omit this field to leave unchanged. Accepts date-only format (e.g., \"2025-10-01\") or ISO timestamp.","example":"2025-10-01","format":"date"},"billedTo":{"type":"string","description":"Billed to date (ISO 8601 date string, YYYY-MM-DD format). If provided, updates the end date that will appear on the final invoice document. This can differ from the actual service period end date (periodTo). Omit this field to leave unchanged. Accepts date-only format (e.g., \"2025-10-31\") or ISO timestamp.","example":"2025-10-31","format":"date"},"contactId":{"type":"object","description":"Contact ID (organization member UUID) who will receive the invoice when it is sent via email. If provided, updates the invoice recipient. Set to null to clear the contact and remove the recipient assignment. Omit this field to leave unchanged. The contact must belong to the project customer organization (validated automatically).","example":"550e8400-e29b-41d4-a716-446655440000","nullable":true}}},"SetTaskBilledTimeDto":{"type":"object","properties":{"value":{"type":"number","description":"Billed time in hours. This is the amount of time that will be charged to the customer, which may differ from the actual time spent. Must be a non-negative number.","example":12.5,"minimum":0}},"required":["value"]},"SetTaskDetailsDto":{"type":"object","properties":{"billedTime":{"type":"number","description":"Billed time in hours. This is the amount of time that will be charged to the customer, which may differ from the actual time spent. Can be null to clear the billed time. Must be a non-negative number if provided.","example":12.5,"nullable":true,"minimum":0},"billedTitle":{"type":"object","description":"Custom billed title for the task. This title will appear on the invoice instead of the task title. Can be null to use the original task title. If provided, must be a non-empty string.","example":"Integration of BIM tools with custom workflow adjustments","nullable":true}}},"SkipFromBillingDto":{"type":"object","properties":{"skipFromBilling":{"type":"boolean","description":"Whether to skip this task from billing. Set to true to exclude the task from the invoice, or false to include it again.","example":true}},"required":["skipFromBilling"]},"AddManualPositionDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the manual position. This ID is used to reference the position in update and delete operations.","example":"550e8400-e29b-41d4-a716-446655440000","format":"uuid"},"title":{"type":"string","description":"Title or description of the manual position (e.g., \"Workshop travel expenses\", \"Translation services\")","example":"Workshop travel expenses"},"price":{"type":"number","description":"Net price (before tax) for this manual position in the workspace currency","example":150}},"required":["id","title","price"]},"UpdateManualPositionDto":{"type":"object","properties":{"title":{"type":"string","description":"Title or description of the manual position","example":"Workshop travel expenses"},"price":{"type":"number","description":"Net price (before tax) for this manual position in the workspace currency","example":150}},"required":["title","price"]},"SaveManualPositionsSortDto":{"type":"object","properties":{"items":{"description":"Array of manual position IDs in the desired sort order. Only custom manual positions (type: manual) can be reordered.","example":["550e8400-e29b-41d4-a716-446655440000","660e8400-e29b-41d4-a716-446655440001"],"items":{"type":"array"},"type":"array"}},"required":["items"]},"SaveSubscriptionSortDto":{"type":"object","properties":{"items":{"description":"Array of subscription billing IDs (billingId) in the desired sort order. The subscriptions will appear on the invoice in this order. All subscription billing IDs from the potential invoice should be included.","example":["550e8400-e29b-41d4-a716-446655440000","660e8400-e29b-41d4-a716-446655440001"],"items":{"type":"array"},"type":"array"}},"required":["items"]},"ChangeQuantityDto":{"type":"object","properties":{"value":{"type":"number","description":"Quantity value for the subscription billing row. For per-unit pricing subscriptions, this sets how many units to bill. Set to null or omit to clear a custom quantity and revert to default. Must be a non-negative integer if provided.","example":5,"nullable":true,"minimum":0}}},"HelpdeskSettingsApiResponse":{"type":"object","properties":{"workspace":{"type":"object","description":"Workspace-wide helpdesk settings including the global enabled flag and default task creation settings. These settings serve as the base configuration for all helpdesk operations in the workspace.","example":{"isHelpdeskEnabled":true,"defaultSettings":{"taskProjectId":"project-uuid-123","taskTypeId":"type-uuid-456","taskStatusId":"status-uuid-789","taskAssignedId":"user-uuid-101","taskPriority":"Normal","sendDefaultReply":true,"defaultReplyBody":"<p>Thank you for contacting us.</p>"}}},"emailAccounts":{"description":"List of all email accounts in the workspace with their helpdesk settings. Each account can have account-specific overrides that override workspace defaults. Accounts without specific settings will use workspace defaults.","example":[{"emailAccountId":"account-uuid-123","emailAddress":"support@example.com","emailDisplayName":"Support Team","isEnabled":true,"taskProjectId":null,"taskTypeId":"type-uuid-456","taskStatusId":null,"taskAssignedId":null,"taskAccountableId":null,"taskTagsIds":null,"taskPriority":null,"sendDefaultReply":null,"defaultReplyBody":null}],"type":"array","items":{"type":"object"}}},"required":["workspace","emailAccounts"]},"UpdateHelpdeskSettingsDto":{"type":"object","properties":{"defaultTaskPriority":{"type":"string","description":"Default task priority for all helpdesk tasks. This priority will be assigned to tasks created from emails unless overridden at the email account or organization level. Valid values: Urgent, High, Normal, Low.","enum":["Low","Normal","High"],"example":"Normal"},"defaultTaskTypeId":{"type":"string","description":"Default task type ID that will be assigned to all helpdesk tasks. This determines the category/type of tasks created from emails. Must be a valid task type ID from your workspace.","example":"type-uuid-123"},"defaultTaskStatusId":{"type":"string","description":"Default task status ID for newly created helpdesk tasks. This determines the initial status of tasks created from emails. Must be a valid task status ID from your workspace.","example":"status-uuid-456"},"defaultReplyBody":{"type":"string","description":"HTML template for automatic replies sent to new email senders. This template is used when `sendDefaultReply` is enabled.\n\nAccepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Helpdesk Variables\n\nYou can use the following variables in your helpdesk templates. Variables are represented as HTML spans:\n\n**taskNumber**: The generated task number for the helpdesk ticket\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"taskNumber\">#12345</span>`\n\n**Note:** The template supports variables that are automatically replaced with actual values (e.g., `taskNumber` becomes the actual task number).","example":"<p>Thank you for contacting us. Your ticket number is <span data-type=\"variable\" data-id=\"taskNumber\">#12345</span>.</p>"},"sendDefaultReply":{"type":"boolean","description":"Whether to automatically send replies to new email senders. When enabled, the system will send the `defaultReplyBody` template as a reply to initial emails from senders who haven't contacted helpdesk before. Replies are only sent for first-time senders, not for replies to existing conversations.","example":true},"autoAssignUserId":{"type":"string","description":"Optional user ID to automatically assign all helpdesk tasks to. If provided, all tasks created from emails will be assigned to this user. If omitted or null, tasks will be created without automatic assignment. Must be a valid user ID from your workspace.","example":"user-uuid-789"}},"required":["defaultTaskPriority","defaultTaskTypeId","defaultTaskStatusId","defaultReplyBody","sendDefaultReply"]},"EmailAccountHelpdeskStatusResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the email account. Use this ID to reference the account in other API calls.","example":"account-uuid-123"},"email":{"type":"string","description":"Email address of the account. This is the address that receives helpdesk emails.","example":"support@example.com"},"displayName":{"type":"string","description":"Display name for the email account. This is the friendly name shown in the UI and in email headers.","example":"Support Team"},"helpdeskEnabled":{"type":"boolean","description":"Whether helpdesk is currently enabled for this email account. When enabled, emails sent to this account will be automatically processed and converted to tasks. When disabled, emails are ignored.","example":true},"settings":{"type":"object","description":"Account-specific helpdesk settings that override workspace defaults. Only present when helpdesk is enabled for this account. Null fields inherit from workspace defaults. If helpdesk is disabled, this field will be undefined.","example":{"emailAccountId":"account-uuid-123","emailAddress":"support@example.com","emailDisplayName":"Support Team","isEnabled":true,"taskProjectId":null,"taskTypeId":"type-uuid-456","taskStatusId":null,"taskAssignedId":null,"taskAccountableId":null,"taskTagsIds":null,"taskPriority":"Normal","sendDefaultReply":true,"defaultReplyBody":"<p>Thank you for contacting us.</p>"}}},"required":["id","email","displayName","helpdeskEnabled"]},"UpdateEmailAccountHelpdeskSettingsDto":{"type":"object","properties":{"defaultTaskPriority":{"type":"string","description":"Task priority override for this email account. If provided, this priority will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default priority. Valid values: Urgent, High, Normal, Low.","enum":["Low","Normal","High"],"example":"High"},"defaultTaskTypeId":{"type":"string","description":"Task type ID override for this email account. If provided, this task type will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default task type. Must be a valid task type ID from your workspace.","example":"type-uuid-override"},"defaultTaskStatusId":{"type":"string","description":"Task status ID override for this email account. If provided, this task status will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default task status. Must be a valid task status ID from your workspace.","example":"status-uuid-override"},"defaultReplyBody":{"type":"string","description":"HTML template override for automatic replies sent from this email account. If provided, this template will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default reply template.\n\nAccepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Helpdesk Variables\n\nYou can use the following variables in your helpdesk templates. Variables are represented as HTML spans:\n\n**taskNumber**: The generated task number for the helpdesk ticket\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"taskNumber\">#12345</span>`\n\n**Note:** Supports the same variables as workspace templates (e.g., `taskNumber`).","example":"<p>Thank you for contacting us. Your ticket number is <span data-type=\"variable\" data-id=\"taskNumber\">#12345</span>.</p>"},"sendDefaultReply":{"type":"object","description":"Auto-reply behavior override for this email account. This field supports 3-state inheritance: `null` (inherit from workspace default), `true` (enable auto-reply for this account), `false` (disable auto-reply for this account). This allows fine-grained control over auto-reply behavior per account.","example":true},"autoAssignUserId":{"type":"string","description":"Auto-assign user override for this email account. If provided, tasks created from emails to this account will be assigned to this user. If omitted or null, the account will inherit the workspace default assignment (or no assignment if workspace default is also null). Set to an empty string to remove the override and use workspace default. Must be a valid user ID from your workspace.","example":"user-uuid-override"}}},"OrganizationHelpdeskSettingsApiResponse":{"type":"object","properties":{"settings":{"type":"object","description":"Organization-level helpdesk settings. These settings allow organizations to override workspace defaults for their email routing. Organization settings have higher priority than workspace defaults and can apply to all emails or only emails from organization members.","example":{"organizationId":"org-uuid-123","organizationName":"Acme Corp","emailAccountSettings":[]}},"emailAccounts":{"description":"List of email accounts with organization-specific helpdesk settings. Each account shows organization-level overrides and includes `effectiveDefaults` which shows the final merged settings (organization + workspace defaults). Organization settings override workspace defaults when emails are processed from organization members (or all senders if mode='all').","example":[{"emailAccountId":"account-uuid-123","emailAddress":"support@example.com","emailDisplayName":"Support Team","organizationMode":"onlyMembers","addressContains":"acme.com","isEnabled":true,"taskProjectId":null,"taskTypeId":null,"taskStatusId":null,"taskAssignedId":null,"taskAccountableId":null,"taskTagsIds":null,"taskPriority":null,"sendDefaultReply":null,"defaultReplyBody":null,"effectiveDefaults":{"taskProjectId":"project-uuid-123","taskTypeId":"type-uuid-456","taskStatusId":"status-uuid-789","taskPriority":"Normal","sendDefaultReply":true,"defaultReplyBody":"<p>Default reply</p>","addressContains":"acme.com"}}],"type":"array","items":{"type":"object"}}},"required":["settings","emailAccounts"]},"UpdateOrganizationHelpdeskSettingsDto":{"type":"object","properties":{"mode":{"type":"string","description":"Organization mode determines when these settings apply. `onlyMembers`: Settings apply only when the email sender is a member of this organization. `all`: Settings apply to ALL emails sent to the account, regardless of sender (highest priority). When set to `all`, these settings override everything including workspace defaults and other organization settings.","enum":["onlyMembers","all"],"example":"onlyMembers"},"defaultTaskPriority":{"type":"string","description":"Default task priority for helpdesk tasks created from emails that match this organization's settings. This overrides workspace defaults when organization settings apply. Valid values: Urgent, High, Normal, Low.","enum":["Low","Normal","High"],"example":"Normal"},"defaultTaskTypeId":{"type":"string","description":"Default task type ID for helpdesk tasks created from emails that match this organization's settings. This overrides workspace defaults when organization settings apply. Must be a valid task type ID from your workspace.","example":"type-uuid-123"},"defaultTaskStatusId":{"type":"string","description":"Default task status ID for helpdesk tasks created from emails that match this organization's settings. This overrides workspace defaults when organization settings apply. Must be a valid task status ID from your workspace.","example":"status-uuid-456"},"defaultReplyBody":{"type":"string","description":"HTML template for automatic replies sent to email senders when organization settings apply. This overrides workspace defaults when organization settings are used.\n\nAccepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Helpdesk Variables\n\nYou can use the following variables in your helpdesk templates. Variables are represented as HTML spans:\n\n**taskNumber**: The generated task number for the helpdesk ticket\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"taskNumber\">#12345</span>`\n\n**Note:** Supports the same variables as workspace templates (e.g., `taskNumber`).","example":"<p>Thank you for contacting us. Your ticket number is <span data-type=\"variable\" data-id=\"taskNumber\">#12345</span>.</p>"},"sendDefaultReply":{"type":"boolean","description":"Whether to automatically send replies to new email senders when organization settings apply. When enabled, the system will send the `defaultReplyBody` template as a reply to initial emails from senders who match this organization's settings.","example":true},"autoAssignUserId":{"type":"string","description":"Optional user ID to automatically assign helpdesk tasks to when organization settings apply. If provided, tasks created from emails that match this organization's settings will be assigned to this user. If omitted or null, tasks will use workspace default assignment or no assignment. Must be a valid user ID from your workspace.","example":"user-uuid-789"}},"required":["mode","defaultTaskPriority","defaultTaskTypeId","defaultTaskStatusId","defaultReplyBody","sendDefaultReply"]},"AppearanceSettingsResponseDto":{"type":"object","properties":{"mainWorkspaceColor":{"type":"string","description":"Main workspace color theme","enum":["DEFAULT","BLACK","RED","ROSE","MAGENTA","GREEN","BLUE","YELLOW","VIOLET"],"example":"BLUE"},"logoUrl":{"type":"object","description":"URL to the workspace logo for default theme","example":"https://app.leadtime.io/api/files/public/123e4567-e89b-12d3-a456-426614174000","nullable":true},"logoDarkThemeUrl":{"type":"object","description":"URL to the workspace logo for dark theme","example":"https://app.leadtime.io/api/files/public/123e4567-e89b-12d3-a456-426614174001","nullable":true}},"required":["mainWorkspaceColor"]},"PatchAppearanceSettingsDto":{"type":"object","properties":{"mainWorkspaceColor":{"type":"string","description":"Main workspace color theme","enum":["DEFAULT","BLACK","RED","ROSE","MAGENTA","GREEN","BLUE","YELLOW","VIOLET"],"example":"BLUE"},"logoId":{"type":"object","description":"File ID for the workspace logo (default theme). Upload file via POST /workspace/upload first.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"logoDarkThemeId":{"type":"object","description":"File ID for the workspace logo (dark theme). Upload file via POST /workspace/upload first.","example":"123e4567-e89b-12d3-a456-426614174001","nullable":true}}},"GetBasicSettingsDto":{"type":"object","properties":{"id":{"type":"string","description":"Workspace ID"},"brandColor":{"type":"string","description":"Brand color in hex format (e.g., #FF5733). Used for workspace branding and internal project colors throughout the application.","example":"#FF5733"},"defaultLanguage":{"type":"string","description":"Default language code for the workspace. Supported values: \"en\" (English) or \"de\" (German). This applies to new users; each user can override this in their profile.","example":"en"},"countryOfOperation":{"type":"string","description":"ISO 3166-1 alpha-2 country code (2 uppercase letters) for the country where the company operates. Example: \"US\" for United States, \"DE\" for Germany.","example":"US"},"timezone":{"type":"string","description":"IANA timezone identifier for the workspace. Defines the timezone used for all time-related information. Example: \"America/New_York\", \"Europe/Berlin\", \"Asia/Tokyo\".","example":"America/New_York"},"attendanceEnabled":{"type":"boolean","description":"Whether attendance tracking (clock in/out) is enabled for employees. When enabled, employees must actively clock in and out. When disabled, working hours are calculated automatically based on contract times."},"timeColorBadPercent":{"type":"number","description":"Lower threshold percentage (1-100) for marking time tracking as \"bad\" or incomplete. Times below this percentage are marked red on the team calendar. Must be less than timeColorGoodPercent.","example":30},"timeColorGoodPercent":{"type":"number","description":"Upper threshold percentage (1-100) for marking time tracking as \"good\" or complete. Times above this percentage are marked green on the team calendar. Must be greater than timeColorBadPercent.","example":70},"defaultTimeFrom":{"format":"date-time","type":"string","description":"Default work start time for employees. Used as a preset when employees clock in. ISO 8601 datetime format. Must be before defaultTimeTo if both are provided."},"defaultTimeTo":{"format":"date-time","type":"string","description":"Default work end time for employees. Used as a preset when employees clock out. ISO 8601 datetime format. Must be after defaultTimeFrom if both are provided."},"timeTrackerAnnoyanceInMin":{"type":"number","description":"Time tracker reminder interval in minutes (5-120). After this many minutes of inactivity, the system reminds users to resume time tracking. Example: 15 means reminders every 15 minutes.","example":15},"sprintPeriodDays":{"type":"number","description":"Sprint duration in days. Defines how long each sprint period lasts. Commonly used values: 7 (one week), 14 (two weeks), or 30 (one month).","example":14},"sprintStartDay":{"type":"number","description":"Sprint start day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period begins.","example":1},"sprintEndDay":{"type":"number","description":"Sprint end day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period ends. If not provided, calculated from sprintPeriodDays.","example":5},"journalNotificationUserIds":{"description":"Array of user IDs (UUIDs) who should receive notifications when journal entries are created or updated. Journal entries document events, notes, or observations for employees, projects, or organizations.","type":"array","items":{"type":"string"}}},"required":["id","defaultLanguage","attendanceEnabled","sprintPeriodDays","sprintStartDay"]},"PatchBasicSettingsDto":{"type":"object","properties":{"brandColor":{"type":"string","description":"Brand color in hex format (e.g., #FF5733). Used for workspace branding and internal project colors throughout the application.","example":"#FF5733"},"defaultLanguage":{"type":"string","description":"Default language code for the workspace. Supported values: \"en\" (English) or \"de\" (German). This applies to new users; each user can override this in their profile.","example":"en"},"countryOfOperation":{"type":"string","description":"ISO 3166-1 alpha-2 country code (2 uppercase letters) for the country where the company operates. Example: \"US\" for United States, \"DE\" for Germany.","example":"US"},"timezone":{"type":"string","description":"IANA timezone identifier for the workspace. Defines the timezone used for all time-related information. Example: \"America/New_York\", \"Europe/Berlin\", \"Asia/Tokyo\".","example":"America/New_York"},"attendanceEnabled":{"type":"boolean","description":"Whether attendance tracking (clock in/out) is enabled for employees. When enabled, employees must actively clock in and out. When disabled, working hours are calculated automatically based on contract times."},"timeColorBadPercent":{"type":"number","description":"Lower threshold percentage (1-100) for marking time tracking as \"bad\" or incomplete. Times below this percentage are marked red on the team calendar. Must be less than timeColorGoodPercent.","example":30},"timeColorGoodPercent":{"type":"number","description":"Upper threshold percentage (1-100) for marking time tracking as \"good\" or complete. Times above this percentage are marked green on the team calendar. Must be greater than timeColorBadPercent.","example":70},"defaultTimeFrom":{"format":"date-time","type":"string","description":"Default work start time for employees. Used as a preset when employees clock in. ISO 8601 datetime format. Must be before defaultTimeTo if both are provided."},"defaultTimeTo":{"format":"date-time","type":"string","description":"Default work end time for employees. Used as a preset when employees clock out. ISO 8601 datetime format. Must be after defaultTimeFrom if both are provided."},"timeTrackerAnnoyanceInMin":{"type":"number","description":"Time tracker reminder interval in minutes (5-120). After this many minutes of inactivity, the system reminds users to resume time tracking. Example: 15 means reminders every 15 minutes.","example":15},"sprintPeriodDays":{"type":"number","description":"Sprint duration in days. Defines how long each sprint period lasts. Commonly used values: 7 (one week), 14 (two weeks), or 30 (one month).","example":14},"sprintStartDay":{"type":"number","description":"Sprint start day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period begins.","example":1},"sprintEndDay":{"type":"number","description":"Sprint end day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period ends. If not provided, calculated from sprintPeriodDays.","example":5},"journalNotificationUserIds":{"description":"Array of user IDs (UUIDs) who should receive notifications when journal entries are created or updated. Journal entries document events, notes, or observations for employees, projects, or organizations.","type":"array","items":{"type":"string"}}}},"HolidayCountryDto":{"type":"object","properties":{"code":{"type":"string","description":"Country or region code in ISO 3166-1 alpha-2 format. For states/provinces, uses format \"COUNTRY-STATE\" (e.g., \"US-CA\" for California).","example":"US"},"name":{"type":"string","description":"Localized country or region name in the requested language. Names are provided in the language specified by the `language` query parameter.","example":"United States"}},"required":["code","name"]},"HolidaySettingsResponseDto":{"type":"object","properties":{"defaultHolidayCode":{"type":"string","description":"The default holiday country/region code for the workspace. This code is automatically applied to all new employees. Individual employees can override this in their profile settings.","example":"US"},"availableCountries":{"description":"Complete list of all available holiday countries and states that can be used for holiday configurations. Use these codes when setting default holiday settings or individual employee holiday rules.","type":"array","items":{"$ref":"#/components/schemas/HolidayCountryDto"}}},"required":["defaultHolidayCode","availableCountries"]},"UpdateHolidaySettingsDto":{"type":"object","properties":{"defaultHolidayCode":{"type":"string","description":"The default holiday country/region code to set for the workspace. Must be one of the available codes from `GET /holidays/countries`. Examples: \"US\" (United States), \"DE\" (Germany), \"US-CA\" (California), \"AT\" (Austria). This setting only affects new employees - existing employees keep their current holiday settings.","example":"US"}},"required":["defaultHolidayCode"]},"HolidayDayDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the holiday day. Only present for holidays in custom or derived years.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"The name of the holiday (e.g., \"Christmas Day\", \"New Year's Day\", \"Company Outing\").","example":"Christmas Day"},"date":{"type":"string","description":"The computed date of the holiday in ISO 8601 format. For Fixed holidays, this is the specific date. For Rule-based holidays, this is the date computed from the rule for the given year.","example":"2024-12-25T00:00:00.000Z"},"type":{"type":"string","description":"The type of holiday: \"Fixed\" for specific dates, \"Rule\" for computed dates based on rules.","enum":["Fixed","Rule"],"example":"Fixed"},"rule":{"type":"string","description":"Rule string for rule-based holidays (only present for Rule type). Examples: \"25 december\", \"easter monday\", \"first monday in september\". The date is automatically computed from this rule for each year.","example":"25 december"},"substitute":{"type":"boolean","description":"Whether this is a substitute holiday (a replacement for a holiday that falls on a weekend). Substitute holidays are automatically created when official holidays fall on non-working days.","example":false}},"required":["id","name","date","type","substitute"]},"HolidayYearResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the holiday year. Only present for custom or derived years. Default years do not have an ID as they cannot be directly modified.","example":"550e8400-e29b-41d4-a716-446655440000"},"year":{"type":"number","description":"The year this holiday configuration applies to.","example":2024},"country":{"type":"string","description":"Country or region code for this holiday configuration (e.g., \"US\", \"DE\", \"US-CA\").","example":"US"},"type":{"type":"string","description":"The type of holiday configuration:\n- `\"default\"`: Official holidays from the holiday data source (cannot be modified)\n- `\"custom\"`: Workspace-specific configuration (can be modified)\n- `\"derived\"`: Configuration inherited from a previous custom year (can be modified)","enum":["default","custom","derived"],"example":"default"},"derivedFrom":{"type":"number","description":"The year this configuration was derived from. Only present for derived type configurations. Indicates which custom year this configuration was copied from.","example":2023},"validTill":{"type":"object","description":"The last year this configuration applies to. If `null`, the configuration applies indefinitely to all future years. Only present for custom or derived years. Controls how the configuration propagates to future years.","example":2025},"holidays":{"description":"Complete list of all holidays for this year, including both fixed and rule-based holidays. Holidays are sorted by date and include computed dates for rule-based holidays.","type":"array","items":{"$ref":"#/components/schemas/HolidayDayDto"}}},"required":["year","country","type","holidays"]},"CreateHolidayYearDto":{"type":"object","properties":{"year":{"type":"number","description":"The year to create a custom holiday configuration for. Must be 1900 or later. If a custom year already exists for this year/country combination, it will be deleted and recreated with default holidays.","example":2024},"country":{"type":"string","description":"Country or region code (e.g., \"US\", \"DE\", \"US-CA\"). Must be one of the available codes from `GET /holidays/countries`.","example":"US"},"validTill":{"type":"object","description":"The last year this configuration applies to. If `null`, the configuration applies indefinitely to all future years. If set to a specific year (e.g., 2026), the configuration applies from the specified year through that year. Future years within this range will automatically be created as \"derived\" configurations. You can update this later using `PUT /holidays/years/:yearId`.","example":2026}},"required":["year","country"]},"UpdateHolidayYearDto":{"type":"object","properties":{"validTill":{"type":"object","description":"The last year this configuration applies to. If `null`, the configuration applies indefinitely to all future years. If set to a specific year (e.g., 2026), the configuration applies from the year's start year through that year. Future years within this range will automatically be created as \"derived\" configurations. Years beyond `validTill` will use default holidays.","example":2026}},"required":["validTill"]},"CreateHolidayDayDto":{"type":"object","properties":{"yearId":{"type":"string","description":"The UUID of the custom holiday year this holiday belongs to. Must be a custom year (created via `POST /holidays/years`). You cannot add holidays to default years.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"The name of the holiday. Should be descriptive and clear (e.g., \"Company Outing\", \"Festivus\", \"Team Building Day\").","example":"Christmas Day"},"type":{"type":"string","description":"Holiday type. Only \"Fixed\" is allowed when creating new holidays. Rule-based holidays can only be edited, not created, as they come from default holiday definitions.","enum":["Fixed"],"example":"Fixed"},"date":{"type":"string","description":"The specific date for the holiday in ISO 8601 format (e.g., \"2024-12-25\" or \"2024-06-15\"). The date should be in the format YYYY-MM-DD. The time portion is ignored.","example":"2024-12-25"}},"required":["yearId","name","type","date"]},"HolidayDayResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the holiday day.","example":"550e8400-e29b-41d4-a716-446655440000"},"yearId":{"type":"string","description":"The UUID of the holiday year this holiday belongs to.","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"The name of the holiday.","example":"Christmas Day"},"type":{"type":"string","description":"The type of holiday: \"Fixed\" for specific dates, \"Rule\" for computed dates based on rules.","enum":["Fixed","Rule"],"example":"Fixed"},"date":{"type":"string","description":"The date of the holiday in ISO 8601 format. Present for Fixed holidays. For Rule-based holidays, this is the computed date based on the rule for the year.","example":"2024-12-25T00:00:00.000Z"},"rule":{"type":"string","description":"Rule string for rule-based holidays. Only present when `type` is \"Rule\". The date is automatically computed from this rule for each year.","example":"25 december"}},"required":["id","yearId","name","type"]},"UpdateHolidayDayDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the holiday. Can be changed to rename the holiday (e.g., \"Christmas\" → \"Holiday Break\").","example":"Christmas Day"},"type":{"type":"string","description":"The type of holiday: \"Fixed\" for specific dates, \"Rule\" for computed dates based on rules. You can convert between types, but converting from Fixed to Rule requires providing a `rule` field.","enum":["Fixed","Rule"],"example":"Fixed"},"date":{"type":"string","description":"The specific date for fixed holidays in ISO 8601 format (e.g., \"2024-12-25\"). Required when `type` is \"Fixed\". The date should be in the format YYYY-MM-DD. The time portion is ignored.","example":"2024-12-25"},"rule":{"type":"string","description":"Rule string for rule-based holidays (only for Rule type). Examples: \"25 december\", \"easter monday\", \"first monday in september\". If not provided when switching to Rule type, the existing rule will be preserved (if available). If converting from Fixed to Rule without an existing rule, you must provide this field.","example":"25 december"}},"required":["name","type"]},"CustomIconResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the custom icon","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Name of the custom icon (e.g., `:company_logo:`, `:team_sales:`). This is the identifier used to reference the icon throughout the workspace.","example":"company_logo"},"imageId":{"type":"string","description":"File ID (UUID) of the icon image file","example":"123e4567-e89b-12d3-a456-426614174000"},"imageUrl":{"type":"string","description":"Full public URL to access and display the icon image. Use this URL to display the icon in your application.","example":"https://app.workc.com/api/files/public/123e4567-e89b-12d3-a456-426614174000"},"authorId":{"type":"string","description":"ID (UUID) of the user who created this custom icon","example":"123e4567-e89b-12d3-a456-426614174000"},"createdAt":{"format":"date-time","type":"string","description":"ISO 8601 date and time when the custom icon was created","example":"2024-01-15T10:30:00Z"},"updatedAt":{"format":"date-time","type":"string","description":"ISO 8601 date and time when the custom icon was last updated (image replacement)","example":"2024-01-15T10:30:00Z"}},"required":["id","name","imageId","imageUrl","authorId","createdAt","updatedAt"]},"CreateCustomIconDto":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for the custom icon. Must be alphanumeric characters and underscores only. Cannot conflict with default emoji names. Typically formatted with colons (e.g., `:company_logo:`, `:team_sales:`, `:lt_editor:`). This name is used to reference the icon throughout the workspace.","example":"company_logo"},"imageId":{"type":"string","description":"File ID (UUID) of the uploaded icon image. First upload the image file (PNG or SVG format) using POST /api/public/workspace/upload, then use the returned file ID here.","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["name","imageId"]},"UpdateCustomIconDto":{"type":"object","properties":{"imageId":{"type":"string","description":"File ID (UUID) of the new icon image. First upload the new image file (PNG or SVG format) using POST /api/public/workspace/upload, then use the returned file ID here. Note: The icon name cannot be changed after creation - only the image can be updated.","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["imageId"]},"TimebookingsGridResponseDto":{"type":"object","properties":{"items":{"type":"array","description":"Array of time booking entries matching the filter criteria. Each item represents a single time log entry with details about when, who, what, and how much time was logged.","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the time booking entry","example":"550e8400-e29b-41d4-a716-446655440000"},"taskId":{"type":"string","nullable":true,"description":"UUID of the task this time was logged to. Null if time was logged directly to a project.","example":"550e8400-e29b-41d4-a716-446655440001"},"projectId":{"type":"string","description":"UUID of the project this time was logged to. Always present.","example":"550e8400-e29b-41d4-a716-446655440002"},"userId":{"type":"string","description":"UUID of the user (employee) who logged this time","example":"550e8400-e29b-41d4-a716-446655440003"},"date":{"type":"string","format":"date-time","description":"Date when the work was performed (ISO 8601 format)","example":"2025-01-15T00:00:00.000Z"},"hours":{"type":"number","description":"Number of hours logged (decimal format, e.g., 8.5 for 8 hours 30 minutes)","example":8.5},"comment":{"type":"string","nullable":true,"description":"Optional comment describing the work performed","example":"Development work on feature X"},"activityId":{"type":"string","description":"UUID of the activity type (e.g., Development, Design, Management)","example":"550e8400-e29b-41d4-a716-446655440004"}}}},"total":{"type":"number","description":"Total number of time booking entries matching the filter criteria across all pages. Use this to calculate total pages: Math.ceil(total / pageSize)","example":150},"totalHours":{"type":"number","description":"Sum of all hours across all filtered time booking entries. This is useful for reporting and calculating total time spent. The value is the sum of the hours field from all matching entries.","example":1250.5},"page":{"type":"number","description":"Current page number (1-based). The first page is page 1.","example":1},"pageSize":{"type":"number","description":"Number of items per page. This is the page size you requested (or the default if not specified). The actual number of items in the items array may be less than this if you are on the last page.","example":100},"totalChildren":{"type":"number","description":"Total number of child items when grouping is enabled. With strict raw-record pagination, this equals total. Only present when grouping is used.","example":250},"groupMeta":{"type":"array","description":"Group metadata when grouping is enabled (e.g. group=day, group=week, group=month). Each entry describes a group on the current page: groupKey, pageFragmentCount, fullGroupCount, pageFragmentSum, fullGroupSum, and continuation flags. Order matches group appearance in items.","items":{"type":"object","properties":{"groupKey":{"type":"string","example":"2026-02-19"},"pageFragmentCount":{"type":"number","example":12},"fullGroupCount":{"type":"number","example":12},"pageFragmentSum":{"type":"number","example":114.3},"fullGroupSum":{"type":"number","example":114.3},"continuedFromPrevious":{"type":"boolean","example":false},"continuesToNext":{"type":"boolean","example":false}}}}},"required":["items","total","totalHours","page","pageSize"]},"CreateTimeBookingDto":{"type":"object","properties":{"type":{"type":"string","description":"Type of time booking. Use \"task\" to log time to a specific task, or \"project\" to log time directly to a project. Task-based bookings are linked to both the task and its parent project. Project-based bookings are only linked to the project.","example":"task","enum":["task","project"]},"id":{"type":"string","description":"UUID of the task to log time to. Required when type is \"task\". The task must exist in your workspace and be linked to a project.","example":"550e8400-e29b-41d4-a716-446655440001"},"projectId":{"type":"string","description":"UUID of the project to log time to. Required when type is \"project\". The project must exist in your workspace.","example":"550e8400-e29b-41d4-a716-446655440002"},"activityId":{"type":"string","description":"UUID of the activity type that describes the work performed. Activities are defined in your workspace settings (e.g., \"Development\", \"Design\", \"Management\", \"Research\"). Activities help categorize time for reporting and analysis.","example":"550e8400-e29b-41d4-a716-446655440004"},"hours":{"type":"number","description":"Number of hours logged. Must be a positive number. Can be a decimal (e.g., 2.5 for 2 hours 30 minutes). If you also provide minutes, they will be added to this value. Required if minutes is not provided or is 0.","example":2.5},"minutes":{"type":"number","description":"Additional minutes to add to the hours. Must be between 0 and 59. These minutes are converted and added to the hours value (e.g., hours: 2, minutes: 30 = 2.5 hours total). Optional - if not provided, defaults to 0.","example":30},"comment":{"type":"string","description":"Optional comment describing what was accomplished during this time. Useful for documentation and later reference. Can be searched in the time tracking grid.","example":"Worked on feature implementation"},"date":{"type":"string","description":"Date when the work was performed. Must be in ISO 8601 date format (YYYY-MM-DD). This is the date the time is attributed to, not necessarily when the booking was created. You can log time retroactively for past dates.","example":"2025-01-15","format":"date"}},"required":["type","activityId","hours","date"]},"PatchTimeBookingDto":{"type":"object","properties":{"type":{"type":"string","description":"Type of time booking. Change to \"task\" to link to a specific task, or \"project\" for project-only booking. If changing type, make sure to provide the corresponding ID field (id for task, projectId for project). If omitted, the existing type is preserved.","example":"task","enum":["task","project"]},"id":{"type":"string","description":"UUID of the task to link this time booking to. Required if type is \"task\". Use this to move time from one task to another, or to change from project-based to task-based booking. If omitted and type is not changed, the existing task link is preserved.","example":"550e8400-e29b-41d4-a716-446655440001"},"projectId":{"type":"string","description":"UUID of the project to link this time booking to. Required if type is \"project\". Use this to move time from one project to another, or to change from task-based to project-based booking. If omitted and type is not changed, the existing project link is preserved.","example":"550e8400-e29b-41d4-a716-446655440002"},"activityId":{"type":"string","description":"UUID of the activity type. Change this to recategorize the work type. If omitted, the existing activity is preserved.","example":"550e8400-e29b-41d4-a716-446655440004"},"hours":{"type":"number","description":"Number of hours to update. Must be a positive number. Can be a decimal (e.g., 3.5 for 3 hours 30 minutes). If you also provide minutes, they will be added to this value. If omitted, the existing hours value is preserved.","example":3},"minutes":{"type":"number","description":"Additional minutes to add to the hours. Must be between 0 and 59. These minutes are converted and added to the hours value. If omitted, defaults to 0 (only the hours value is used).","example":0},"comment":{"type":"string","description":"Comment text. Update this to change or add notes about the work performed. If omitted, the existing comment is preserved. To clear a comment, provide an empty string.","example":"Updated comment"},"date":{"type":"string","description":"Date when the work was performed. Must be in ISO 8601 date format (YYYY-MM-DD). Use this to correct the date if the time was logged to the wrong day. If omitted, the existing date is preserved.","example":"2025-01-15","format":"date"}}},"TimeTrackerIntervalDto":{"type":"object","properties":{"start":{"type":"string","description":"Interval start time in ISO 8601 format (UTC). This is when the time tracking period began.","example":"2024-01-15T10:00:00Z"},"end":{"type":"string","description":"Interval end time in ISO 8601 format (UTC). For active (running) intervals, this is set to the current time. For completed intervals, this marks when tracking stopped. Intervals that span midnight are automatically ended at end of day.","example":"2024-01-15T11:30:00Z"}},"required":["start","end"]},"TimeTrackerResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the time tracker (UUID)","example":"550e8400-e29b-41d4-a716-446655440000"},"timeTrackingType":{"type":"string","description":"Type of time tracking: Task (tracking time on a specific task) or Project (tracking time at project level). Determines whether taskId is required.","enum":["Task","Project"],"example":"Task"},"projectId":{"type":"object","description":"UUID of the project this tracker is associated with. Can be null if tracking general work.","example":"550e8400-e29b-41d4-a716-446655440001","nullable":true},"taskId":{"type":"object","description":"UUID of the task this tracker is associated with. Only used when timeTrackingType is Task. Can be null for project-level tracking.","example":"550e8400-e29b-41d4-a716-446655440002","nullable":true},"activityId":{"type":"object","description":"UUID of the activity category (e.g., Development, Testing, Management). Used for categorizing work in reports. Can be null.","example":"550e8400-e29b-41d4-a716-446655440003","nullable":true},"comment":{"type":"object","description":"Optional comment or description of the work being tracked. Helps provide context when reviewing time entries later.","example":"Working on feature implementation","nullable":true},"correction":{"type":"number","description":"Manual time adjustment in minutes. Positive values add time, negative values subtract time. Useful for correcting tracking errors or adding time that was not tracked. Default is 0.","example":0},"createdAt":{"type":"string","description":"Timestamp when the tracker was created, in ISO 8601 format (UTC)","example":"2024-01-15T10:00:00Z"},"intervals":{"description":"Array of time intervals that make up the total tracked time. Each interval represents a continuous period of tracking (start to end). If an interval has no end, it is currently active. Intervals are automatically ended at end of day if they span midnight.","type":"array","items":{"$ref":"#/components/schemas/TimeTrackerIntervalDto"}},"isRunning":{"type":"boolean","description":"Whether the tracker currently has an active (running) interval. True if any interval has no end time, meaning time is being tracked right now.","example":true},"hours":{"type":"number","description":"Total tracked time in hours, calculated from all intervals plus any correction. Minimum value is 0.0166667 (1 minute).","example":2.5},"minutesPadded":{"type":"string","description":"Minutes portion of the tracked time, formatted as a zero-padded string (e.g., \"05\", \"30\", \"59\")","example":"30"},"hoursPadded":{"type":"string","description":"Hours portion of the tracked time, formatted as a zero-padded string (e.g., \"02\", \"12\", \"00\")","example":"02"},"autoEnded":{"type":"boolean","description":"Whether any interval was automatically ended at the end of the day. This happens when a running interval started on a previous day and was not manually stopped.","example":false},"isOld":{"type":"boolean","description":"Whether the tracker contains intervals from previous days. True if any interval start time is not from today.","example":false}},"required":["id","timeTrackingType","projectId","taskId","activityId","comment","correction","createdAt","intervals","isRunning","hours","minutesPadded","hoursPadded","autoEnded","isOld"]},"TrackerListResponseDto":{"type":"object","properties":{"trackers":{"description":"Array of all time trackers belonging to the authenticated user, sorted by creation time. Each tracker includes calculated fields like isRunning, hours, and formatted time strings.","type":"array","items":{"$ref":"#/components/schemas/TimeTrackerResponseDto"}},"lastInteraction":{"type":"string","description":"Timestamp of the last interaction with any tracker (create, update, pause, resume, etc.) in ISO 8601 format (UTC). Used to track user activity. Can be null if no trackers exist.","example":"2024-01-15T13:00:00Z","nullable":true}},"required":["trackers","lastInteraction"]},"TimeTrackerCreateDto":{"type":"object","properties":{"timeTrackingType":{"type":"string","description":"Required. Type of time tracking: Task (for tracking time on a specific task) or Project (for tracking time at project level). Determines whether taskId should be provided.","enum":["Task","Project"],"example":"Task"},"projectId":{"type":"string","description":"Optional. UUID of the project to associate with this tracker. If provided, the tracked time will be linked to this project.","example":"550e8400-e29b-41d4-a716-446655440001"},"taskId":{"type":"string","description":"Optional. UUID of the task to associate with this tracker. Only used when timeTrackingType is Task. If timeTrackingType is Task, you should typically provide a taskId.","example":"550e8400-e29b-41d4-a716-446655440002"},"activityId":{"type":"string","description":"Optional. UUID of the activity category (e.g., Development, Testing, Management). Used to categorize the type of work being tracked for reporting purposes.","example":"550e8400-e29b-41d4-a716-446655440003"},"comment":{"type":"string","description":"Optional. Free-text comment describing the work being tracked. Helps provide context when reviewing time entries later.","example":"Working on feature implementation"},"correction":{"type":"number","description":"Optional. Manual time adjustment in minutes. Positive values add time (e.g., if you started tracking late), negative values subtract time. Default is 0.","example":0}},"required":["timeTrackingType"]},"TimeTrackerUpdateDto":{"type":"object","properties":{"timeTrackingType":{"type":"string","description":"Optional. Change the time tracking type between Task and Project. If changed, ensure taskId is appropriate for the new type.","enum":["Task","Project"],"example":"Task"},"projectId":{"type":"object","description":"Optional. Update or remove the project association. Provide a UUID to change the project, or null to remove the association. Only provided fields are updated.","example":"550e8400-e29b-41d4-a716-446655440001","nullable":true},"taskId":{"type":"object","description":"Optional. Update or remove the task association. Provide a UUID to change the task, or null to remove the association. Only used when timeTrackingType is Task.","example":"550e8400-e29b-41d4-a716-446655440002","nullable":true},"activityId":{"type":"object","description":"Optional. Update or remove the activity category. Provide a UUID to change the activity, or null to remove it.","example":"550e8400-e29b-41d4-a716-446655440003","nullable":true},"comment":{"type":"object","description":"Optional. Update or remove the comment. Provide a string to update the comment, or null to remove it.","example":"Updated comment","nullable":true},"correction":{"type":"number","description":"Optional. Adjust the tracked time by providing a correction in minutes. Positive values add time, negative values subtract time. This affects the total hours calculation.","example":5}}},"GetCompanySettingsDto":{"type":"object","properties":{"companyName":{"type":"string","description":"Official business name of the company. Used in documents, invoices, contracts, and projects throughout the system.","example":"Acme Corporation"},"companyLegalForm":{"type":"string","description":"Legal form or structure of the company (e.g., GmbH, AG, LLC, Inc., sole proprietorship). Used in legal documents and official communications.","example":"GmbH"},"companyStreet":{"type":"string","description":"Street name of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"123 Main St"},"companyZip":{"type":"string","description":"ZIP or postal code of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"10115"},"companyCity":{"type":"string","description":"City of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"Berlin"},"companyCountry":{"type":"string","description":"Country code (ISO 3166-1 alpha-2 format, e.g., \"DE\", \"US\") of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"DE"},"companyHouseNumber":{"type":"string","description":"House number of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"42"},"companyPhone":{"type":"string","description":"Official company phone number. Used for contact information in documents and communications.","example":"+49 30 12345678"},"companyEmail":{"type":"string","description":"Main business contact email address. Used for contact information in documents and communications.","example":"contact@acme.com"},"companyWebsite":{"type":"string","description":"Company website URL (optional). Used for contact information in documents and communications.","example":"https://acme.com"},"companyFax":{"type":"string","description":"Company fax number (optional). Used for contact information in documents and communications. For companies that still use fax.","example":"+49 30 87654321"},"companyTaxNumber":{"type":"string","description":"Tax identification number. Required for invoices and tax documents. Format varies by country (e.g., \"DE123456789\" for Germany).","example":"DE123456789"},"companyRegistrationNumber":{"type":"string","description":"Business registration number or commercial registry entry number. Format varies by country (e.g., \"HRB 12345\" for German companies).","example":"HRB 12345"},"companyRegistrationCourt":{"type":"string","description":"Responsible registration court where the company is registered. Example: \"Amtsgericht Berlin\" for German companies.","example":"Amtsgericht Berlin"},"companyShortDescription":{"type":"string","description":"Short description of what the company does. Optional field to describe the company's business activities. Example: \"Manufacturer of Leadtime – an ERP system for software providers.\""},"invoiceAccountNumber":{"type":"string","description":"Bank account number in IBAN format used for invoices. This account number appears on invoices for payment purposes.","example":"DE89370400440532013000"}},"required":["companyName"]},"PatchCompanySettingsDto":{"type":"object","properties":{"companyName":{"type":"string","description":"Official business name of the company. Used in documents, invoices, contracts, and projects throughout the system.","example":"Acme Corporation"},"companyLegalForm":{"type":"string","description":"Legal form or structure of the company (e.g., GmbH, AG, LLC, Inc., sole proprietorship). Used in legal documents and official communications.","example":"GmbH"},"companyStreet":{"type":"string","description":"Street name of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"123 Main St"},"companyZip":{"type":"string","description":"ZIP or postal code of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"10115"},"companyCity":{"type":"string","description":"City of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"Berlin"},"companyCountry":{"type":"string","description":"Country code (ISO 3166-1 alpha-2 format, e.g., \"DE\", \"US\") of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"DE"},"companyHouseNumber":{"type":"string","description":"House number of the company address. Part of the complete address used on invoices, contracts, and other documents.","example":"42"},"companyPhone":{"type":"string","description":"Official company phone number. Used for contact information in documents and communications.","example":"+49 30 12345678"},"companyEmail":{"type":"string","description":"Main business contact email address. Used for contact information in documents and communications. Must be valid email format.","example":"contact@acme.com"},"companyWebsite":{"type":"string","description":"Company website URL (optional). Used for contact information in documents and communications. Must be valid URL format.","example":"https://acme.com"},"companyFax":{"type":"string","description":"Company fax number (optional). Used for contact information in documents and communications. For companies that still use fax.","example":"+49 30 87654321"},"companyTaxNumber":{"type":"string","description":"Tax identification number. Required for invoices and tax documents. Format varies by country (e.g., \"DE123456789\" for Germany).","example":"DE123456789"},"companyRegistrationNumber":{"type":"string","description":"Business registration number or commercial registry entry number. Format varies by country (e.g., \"HRB 12345\" for German companies).","example":"HRB 12345"},"companyRegistrationCourt":{"type":"string","description":"Responsible registration court where the company is registered. Example: \"Amtsgericht Berlin\" for German companies.","example":"Amtsgericht Berlin"},"companyShortDescription":{"type":"string","description":"Short description of what the company does. Optional field to describe the company's business activities. Example: \"Manufacturer of Leadtime – an ERP system for software providers.\""},"invoiceAccountNumber":{"type":"string","description":"Bank account number in IBAN format used for invoices. This account number appears on invoices for payment purposes.","example":"DE89370400440532013000"}}},"OrganizationMemberJournalResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for this journal entry","example":"550e8400-e29b-41d4-a716-446655440000"},"memberId":{"type":"string","description":"UUID of the organization member (external contact) this journal entry is associated with. This identifies which external contact the entry is about.","example":"550e8400-e29b-41d4-a716-446655440000"},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when this journal entry was created. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., \"2024-01-15T10:30:00Z\").","example":"2024-01-15T10:30:00Z"},"createdBy":{"type":"string","description":"UUID of the user who created this journal entry. This identifies who wrote the entry, which is useful for tracking authorship and accountability.","example":"550e8400-e29b-41d4-a716-446655440000"},"lastUpdated":{"type":"string","description":"ISO 8601 timestamp indicating when this journal entry was last modified. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., \"2024-01-16T14:20:00Z\"). This is automatically updated whenever the entry is edited via the PATCH endpoint.","example":"2024-01-16T14:20:00Z"},"mood":{"type":"string","description":"Emotional indicator for the journal entry. Values: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). This helps categorize and filter entries by emotional context.","enum":["Sad","Neutral","Happy"],"example":"Neutral"},"reminder":{"type":"object","description":"Optional reminder date in ISO 8601 format (YYYY-MM-DD). Set when creating or updating the entry to track follow-up dates for goals, agreements, or actions. Returns `null` if no reminder is set. Example: \"2024-12-31\" for December 31, 2024.","example":"2024-12-31","nullable":true},"body":{"type":"string","description":"Journal entry content in HTML format. The content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications. Contains the rich text content including headings, paragraphs, lists, links, and formatting as originally provided.","example":"<h1>Member Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>"}},"required":["id","memberId","createdAt","createdBy","lastUpdated","mood","reminder","body"]},"CreateOrganizationMemberJournalDto":{"type":"object","properties":{"memberId":{"type":"string","description":"UUID of the organization member (external contact) this journal entry is about. The member must exist in the workspace and be accessible to the current user. Organization members can only create entries for their own member record unless they have the `Organizations.writeJournal` permission.","example":"550e8400-e29b-41d4-a716-446655440000"},"body":{"type":"string","description":"Rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n Use this field to document conversations, observations, agreements, or any notes about the organization member.","example":"<h1>Member Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>"},"mood":{"type":"string","description":"Emotional indicator for the journal entry. Use this to quickly categorize the tone or sentiment of the interaction. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). Defaults to `Neutral` if not provided. This helps filter and analyze journal entries by emotional context.","enum":["Sad","Neutral","Happy"],"example":"Neutral"},"reminder":{"type":"string","description":"Optional reminder date in ISO 8601 format (YYYY-MM-DD). Use this to set a follow-up date for reviewing goals, agreements, or taking action related to this journal entry. The reminder date can be used to filter entries and trigger notifications. Example: \"2024-12-31\" for December 31, 2024. If not provided, no reminder is set.","example":"2024-12-31"}},"required":["memberId","body"]},"PatchOrganizationMemberJournalDto":{"type":"object","properties":{"memberId":{"type":"string","description":"UUID of the organization member to associate this journal entry with. If provided, changes which member this entry is about. The new member must exist and be accessible. If the new member is the current user's own member record, `visibleToSubject` will be automatically set to `true`. If omitted, the existing member association is preserved.","example":"550e8400-e29b-41d4-a716-446655440000"},"body":{"type":"string","description":"Updated rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n If omitted, the existing body content remains unchanged.","example":"<h1>Updated Member Status</h1><p>Follow-up meeting scheduled for next week.</p>"},"mood":{"type":"string","description":"Updated emotional indicator for the journal entry. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). If omitted, the existing mood value remains unchanged.","enum":["Sad","Neutral","Happy"],"example":"Happy"},"reminder":{"type":"object","description":"Updated reminder date in ISO 8601 format (YYYY-MM-DD). Provide a date string (e.g., \"2024-12-31\") to set or update the reminder. Provide `null` to clear an existing reminder. If omitted entirely, the existing reminder remains unchanged. Use reminders to track follow-up dates for goals, agreements, or actions related to this journal entry.","example":"2024-12-31","nullable":true}}},"OrganizationMemberResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Organization member ID","example":"123e4567-e89b-12d3-a456-426614174000"},"organizationId":{"type":"string","description":"Organization ID","example":"123e4567-e89b-12d3-a456-426614174000"},"userId":{"type":"string","description":"Associated user ID","example":"123e4567-e89b-12d3-a456-426614174000"},"firstName":{"type":"string","description":"First name","example":"John"},"lastName":{"type":"string","description":"Last name","example":"Doe"},"email":{"type":"string","description":"Email address","example":"john.doe@example.com"},"roleId":{"type":"object","description":"User role ID that determines the member's access permissions in Leadtime. Can be `null` if the member does not have login access.","example":"workspace-id_guest_","nullable":true},"isActive":{"type":"boolean","description":"Whether member is active","example":true},"canLogin":{"type":"boolean","description":"Whether member can login to the system","example":true},"gender":{"type":"string","description":"Gender","enum":["Male","Female"],"example":"Male","nullable":true},"title":{"type":"object","description":"Title/salutation","example":"Dr.","nullable":true},"degree":{"type":"object","description":"Academic degree","example":"Ph.D.","nullable":true},"position":{"type":"object","description":"Position/role in organization","example":"Manager","nullable":true},"addressStreet":{"type":"object","description":"Street address","example":"Main Street","nullable":true},"addressZip":{"type":"object","description":"ZIP/postal code","example":"12345","nullable":true},"addressCity":{"type":"object","description":"City","example":"New York","nullable":true},"addressCountry":{"type":"object","description":"Country","example":"USA","nullable":true},"addressHouseNumber":{"type":"object","description":"House number","example":"123A","nullable":true},"phone":{"type":"object","description":"Phone number","example":"+1234567890","nullable":true},"birthDate":{"type":"object","description":"Date of birth in ISO 8601 date format (YYYY-MM-DD). Returns `null` if not set.","example":"1990-01-15","nullable":true},"socialNetworkLink":{"type":"object","description":"Social network profile link","example":"https://linkedin.com/in/johndoe","nullable":true},"influenceLevel":{"type":"string","description":"Influence level classification","enum":["Low","Medium","High","Dominant"],"nullable":true},"attitudeLevel":{"type":"string","description":"Attitude level classification","enum":["Enthusiastic","WellMeaning","Neutral","Opposing"],"nullable":true},"personalityType":{"type":"string","description":"Personality type classification","enum":["Dominant","Conscientious","Steady","Initiative"],"nullable":true},"avatarUrl":{"type":"object","description":"Public URL to the member's avatar/profile picture. Returns `null` if no avatar has been uploaded. The URL can be used directly in image tags or for display purposes.","example":"https://example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when the organization member was created.","example":"2024-01-15T10:30:00.000Z"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp indicating when the organization member was last updated.","example":"2024-01-20T15:45:00.000Z"}},"required":["id","organizationId","userId","firstName","lastName","email","roleId","isActive","canLogin","gender","title","degree","position","addressStreet","addressZip","addressCity","addressCountry","addressHouseNumber","phone","birthDate","socialNetworkLink","influenceLevel","attitudeLevel","personalityType","avatarUrl","createdAt","updatedAt"]},"CreateOrganizationMemberDto":{"type":"object","properties":{"organizationId":{"type":"string","description":"UUID of the organization this member belongs to. The organization must exist in your workspace.","example":"123e4567-e89b-12d3-a456-426614174000"},"firstName":{"type":"string","description":"First name","example":"John"},"lastName":{"type":"string","description":"Last name","example":"Doe"},"email":{"type":"string","description":"Email address of the member. This email will be used for the associated user account and for sending invitation emails if login access is enabled. Must be a valid email format.","example":"john.doe@example.com"},"roleId":{"type":"string","description":"User role ID that determines the member's access permissions in Leadtime. Common roles include guest roles (e.g., `workspace-id_guest_`) for external users. This role is applied to the automatically created user account.","example":"workspace-id_guest_"},"isActive":{"type":"boolean","description":"Whether the member is active in the system. Inactive members are hidden from normal views but remain in the database. Defaults to `true`.","example":true,"default":true},"canLogin":{"type":"boolean","description":"Whether the member can log in to Leadtime as a guest user. If `true`, a user account is created with Active status and an invitation email is sent. If `false` or omitted, the member exists as a contact only with an Inactive user account. Defaults to `false`.","example":true},"gender":{"type":"string","description":"Gender","enum":["Male","Female"],"example":"Male"},"title":{"type":"string","description":"Title/salutation (e.g., Dr., Prof.)","example":"Dr."},"degree":{"type":"string","description":"Academic degree","example":"Ph.D."},"position":{"type":"string","description":"Job title or position within the organization (e.g., \"Managing Director\", \"Project Manager\", \"Head of Sales\").","example":"Manager"},"addressStreet":{"type":"string","description":"Street address","example":"Main Street"},"addressZip":{"type":"string","description":"ZIP/postal code","example":"12345"},"addressCity":{"type":"string","description":"City","example":"New York"},"addressCountry":{"type":"string","description":"Country","example":"USA"},"addressHouseNumber":{"type":"string","description":"House number","example":"123A"},"phone":{"type":"string","description":"Phone number","example":"+1234567890"},"birthDate":{"format":"date-time","type":"string","description":"Birth date (ISO 8601 date format: YYYY-MM-DD)","example":"1990-01-15"},"socialNetworkLink":{"type":"string","description":"Social network profile link","example":"https://linkedin.com/in/johndoe"},"influenceLevel":{"type":"string","description":"Influence level classification","enum":["Low","Medium","High","Dominant"]},"attitudeLevel":{"type":"string","description":"Attitude level classification","enum":["Enthusiastic","WellMeaning","Neutral","Opposing"]},"personalityType":{"type":"string","description":"Personality type classification","enum":["Dominant","Conscientious","Steady","Initiative"]},"avatarId":{"type":"string","description":"Avatar file ID (upload via POST /workspace/upload first)","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["organizationId","firstName","lastName","email","roleId","isActive"]},"PatchOrganizationMemberDto":{"type":"object","properties":{"firstName":{"type":"string","description":"First name. If provided, this change will automatically sync to the associated user account.","example":"John"},"lastName":{"type":"string","description":"Last name. If provided, this change will automatically sync to the associated user account.","example":"Doe"},"email":{"type":"string","description":"Email address. If provided, this change will automatically sync to the associated user account. Must be a valid email format.","example":"john.doe@example.com"},"roleId":{"type":"string","description":"User role ID that determines access permissions. If provided, this change will automatically sync to the associated user account, updating their permissions in Leadtime.","example":"workspace-id_staff_"},"isActive":{"type":"boolean","description":"Whether the member is active in the system. Inactive members are hidden from normal views but remain in the database.","example":true},"canLogin":{"type":"boolean","description":"Whether the member can log in to Leadtime. Changing this value will update the associated user account: setting to `true` activates the user and sends an invitation email; setting to `false` deactivates the user and clears the invite token.","example":true},"gender":{"type":"string","description":"Gender of the member. Used for personalized greetings in letters and communications. Available values: `Male`, `Female`.","enum":["Male","Female"],"example":"Male"},"title":{"type":"string","description":"Title or salutation prefix (e.g., \"Dr.\", \"Prof.\", \"Mr.\", \"Mrs.\"). Used in formal communications and letters.","example":"Dr."},"degree":{"type":"string","description":"Academic degree or qualification (e.g., \"Ph.D.\", \"MBA\", \"M.Sc.\"). Used in formal communications.","example":"Ph.D."},"position":{"type":"string","description":"Job title or position within the organization (e.g., \"Managing Director\", \"Project Manager\", \"Head of Sales\").","example":"Senior Manager"},"addressStreet":{"type":"string","description":"Street name for the member's address. Part of the complete address information.","example":"Main Street"},"addressZip":{"type":"string","description":"ZIP or postal code for the member's address. Part of the complete address information.","example":"12345"},"addressCity":{"type":"string","description":"City for the member's address. Part of the complete address information.","example":"New York"},"addressCountry":{"type":"string","description":"Country for the member's address. Part of the complete address information.","example":"USA"},"addressHouseNumber":{"type":"string","description":"House or building number for the member's address. Part of the complete address information.","example":"123A"},"phone":{"type":"string","description":"Phone number for contacting the member. Can include country code prefix (e.g., +1 for US).","example":"+1234567890"},"birthDate":{"format":"date-time","type":"string","description":"Date of birth in ISO 8601 date format (YYYY-MM-DD). Used for birthday reminders and age calculations.","example":"1990-01-15"},"socialNetworkLink":{"type":"string","description":"URL to a social network profile (e.g., LinkedIn, Twitter, XING). Used for relationship management and contact information.","example":"https://linkedin.com/in/johndoe"},"influenceLevel":{"type":"string","description":"Classification of the member's level of influence within their organization. Used for relationship management and strategic planning. Available values: `Low`, `Medium`, `High`, `Dominant`.","enum":["Low","Medium","High","Dominant"]},"attitudeLevel":{"type":"string","description":"Classification of the member's attitude towards your company. Used for relationship management and communication strategy. Available values: `Enthusiastic`, `WellMeaning`, `Neutral`, `Opposing`.","enum":["Enthusiastic","WellMeaning","Neutral","Opposing"]},"personalityType":{"type":"string","description":"Personality type classification based on behavioral patterns. Used for tailoring communication and interaction approaches. Available values: `Dominant`, `Conscientious`, `Steady`, `Initiative`.","enum":["Dominant","Conscientious","Steady","Initiative"]},"avatarId":{"type":"string","description":"UUID of the avatar/profile picture file. To update: upload via `POST /workspace/upload` first, then provide the file ID. To remove: set to `null` or empty string. If not provided, the avatar remains unchanged.","example":"123e4567-e89b-12d3-a456-426614174000"}}},"OrgLetterAdressType":{"type":"string","enum":["Organization","Member"],"description":"The address type used for this letter. \"Organization\" means the organization's main address was used, \"Member\" means a member's private address was used."},"LetterResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) of the organization letter","example":"123e4567-e89b-12d3-a456-426614174000"},"organizationId":{"type":"string","description":"UUID of the organization this letter belongs to. Use this to link the letter to the correct organization.","example":"123e4567-e89b-12d3-a456-426614174001"},"memberId":{"type":"object","description":"UUID of the organization member (contact person) this letter is addressed to. Will be null if the letter is addressed to the organization itself rather than a specific member.","example":"123e4567-e89b-12d3-a456-426614174002","nullable":true},"subject":{"type":"string","description":"The subject or title of the letter as displayed in listings and on the document.","example":"Invoice Reminder"},"dateOfLetter":{"type":"string","description":"The date shown on the letter in ISO 8601 format (YYYY-MM-DDTHH:mm:ss.sssZ). This is the date that appears on the letter document itself, which may differ from the creation date.","example":"2024-01-15T00:00:00.000Z","format":"date-time"},"addressType":{"description":"The address type used for this letter. \"Organization\" means the organization's main address was used, \"Member\" means a member's private address was used.","example":"Organization","allOf":[{"$ref":"#/components/schemas/OrgLetterAdressType"}]},"body":{"type":"string","description":"The letter body content in HTML format. This has been converted from the internal IDoc storage format to HTML for easy display and processing. Includes all formatting, template variables (as HTML spans), and rich text elements.","example":"<h1>Dear Customer</h1><p>This is a reminder...</p>"},"createdAt":{"type":"string","description":"ISO 8601 timestamp (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when the letter was first created in the system.","example":"2024-01-10T10:30:00.000Z","format":"date-time"},"lastUpdated":{"type":"string","description":"ISO 8601 timestamp (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when the letter was last modified. This is automatically updated whenever any field of the letter is changed.","example":"2024-01-12T14:20:00.000Z","format":"date-time"},"createdBy":{"type":"string","description":"UUID of the user who created this letter. Use this to track authorship and for audit purposes.","example":"123e4567-e89b-12d3-a456-426614174003"}},"required":["id","organizationId","memberId","subject","dateOfLetter","addressType","body","createdAt","lastUpdated","createdBy"]},"CreateLetterDto":{"type":"object","properties":{"organizationId":{"type":"string","description":"The UUID of the organization this letter belongs to. The organization must exist in your workspace and be accessible with your permissions.","example":"123e4567-e89b-12d3-a456-426614174000"},"memberId":{"type":"object","description":"Optional UUID of the organization member (contact person) this letter is addressed to. If provided, the member must belong to the specified organization. Leave empty or set to null for letters addressed to the organization itself.","example":"123e4567-e89b-12d3-a456-426614174001","nullable":true},"subject":{"type":"string","description":"The subject or title of the letter. This appears in letter listings and as the document title when exporting to PDF.","example":"Invoice Reminder","minLength":1},"dateOfLetter":{"type":"string","description":"The date shown on the letter (ISO 8601 date format: YYYY-MM-DD). This can be backdated if needed. The date is displayed on the letter document and used for sorting and filtering.","example":"2024-01-15","format":"date"},"addressType":{"description":"Determines which address is used on the letter. Use \"Organization\" for the organization's main address, or \"Member\" for the member's private address (requires memberId to be set).","example":"Organization","allOf":[{"$ref":"#/components/schemas/OrgLetterAdressType"}]},"body":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your organization letter body. Variables are represented as HTML spans:\n\n`clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`, `<span data-type=\"variable\" data-id=\"contactPerson\">John Doe</span>`","example":"<h1>Dear Customer</h1><p>This is a reminder for invoice #12345...</p><p><span data-type=\"variable\" data-id=\"signature\"></span></p>"}},"required":["organizationId","subject","dateOfLetter","addressType","body"]},"PatchLetterDto":{"type":"object","properties":{"organizationId":{"type":"string","description":"Optional: The UUID of the organization this letter belongs to. If provided, changes which organization the letter is linked to. The organization must exist and be accessible in your workspace.","example":"123e4567-e89b-12d3-a456-426614174000"},"memberId":{"type":"object","description":"Optional: The UUID of the organization member this letter is addressed to. If provided, links the letter to that member (member must belong to the specified or current organization). Set to null to clear the member association. Omit this field to leave it unchanged.","example":"123e4567-e89b-12d3-a456-426614174001","nullable":true},"subject":{"type":"string","description":"Optional: The subject or title of the letter. If provided, updates the letter subject. Omit to leave unchanged.","example":"Updated Invoice Reminder"},"dateOfLetter":{"type":"string","description":"Optional: The date shown on the letter (ISO 8601 date format: YYYY-MM-DD). If provided, updates the letter date. Omit to leave unchanged.","example":"2024-01-15","format":"date"},"addressType":{"description":"Optional: The address type for the letter. Use \"Organization\" for the organization's main address, or \"Member\" for the member's private address. If provided, updates the address type. Omit to leave unchanged.","example":"Member","allOf":[{"$ref":"#/components/schemas/OrgLetterAdressType"}]},"body":{"type":"string","description":"Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**variable**\nType: inline\nDocument/template variable token (span with data-type=\"variable\" and data-id).\n- id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.\nAtts:\n- id: null\n```html\n<span data-type=\"variable\" data-id=\"null/some-value\" id=\"null/some-value\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n\n\n## Available Template Variables\n\nYou can use the following variables in your organization letter body. Variables are represented as HTML spans:\n\n`clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`\n\n**Example HTML**: `<span data-type=\"variable\" data-id=\"clientCompanyName\">ACME Corp</span>`, `<span data-type=\"variable\" data-id=\"contactPerson\">John Doe</span>`","example":"<p>Updated content with <span data-type=\"variable\" data-id=\"signature\"></span></p>"}}},"OrganizationCustomFieldTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"string","description":"Translated name of the custom field in this language. Used for multilingual display in organization forms.","example":"Priority Level"},"description":{"type":"string","description":"Translated description of the custom field in this language. Provides context about what information should be entered.","example":"Priority level for the organization"}},"required":["language"]},"OrganizationCustomFieldSelectOptionTranslationDto":{"type":"object","properties":{"language":{"type":"string","description":"ISO 639-1 language code for the translation (e.g., \"en\" for English, \"de\" for German)","example":"en"},"name":{"type":"string","description":"Translated display name for the select option in this language. Shown to users when selecting from dropdown options.","example":"High Priority"}},"required":["language","name"]},"OrganizationCustomFieldSelectOptionDto":{"type":"object","properties":{"id":{"type":"string","description":"Optional unique identifier for the select option. If not provided, the value will be used as the identifier. Required when updating existing options.","example":"option-1"},"value":{"type":"string","description":"Display value for the select option. This is the value stored when the option is selected. Must be unique within the field.","example":"High Priority"},"translations":{"description":"Array of translations for multilingual support. Each translation contains a language code and translated display name. The system automatically shows the correct language based on user preferences.","example":[{"language":"en","name":"High Priority"},{"language":"de","name":"Hohe Priorität"}],"type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldSelectOptionTranslationDto"}}},"required":["value","translations"]},"OrganizationCustomFieldResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for the custom field. Used to reference this field when setting values on organizations.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Display name of the custom field. Shown as the field label in organization forms.","example":"Priority Level"},"description":{"type":"string","description":"Description providing context about what information should be entered in this field.","example":"Priority level for the organization"},"sort":{"type":"number","description":"Numeric sort order for display purposes. Lower numbers appear first in organization forms. Determines the order in which fields are displayed.","example":1},"type":{"type":"string","description":"Field type enum value indicating the input type. Valid values: Text, Textarea, Number, Date, Checkbox, Select, MultiSelect.","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"entity":{"type":"string","description":"Entity type identifier. Always \"Organization\" for organization custom fields. Used internally to scope fields to specific entity types.","example":"Organization"},"translations":{"description":"Complete array of translations for the field name. Each translation contains the language code and localized name.","type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldTranslationDto"}},"selectOptions":{"description":"Array of select options for Select and MultiSelect field types. Each option contains an ID, value, and translations. Empty array for other field types.","type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldSelectOptionDto"}}},"required":["id","name","description","sort","type","entity","translations","selectOptions"]},"CreateOrganizationCustomFieldDto":{"type":"object","properties":{"id":{"type":"string","description":"Optional unique identifier for the custom field. Only needed when updating an existing field. If omitted, a new field will be created."},"name":{"type":"string","description":"Display name for the custom field. Shown as the field label in organization forms (e.g., \"Priority Level\", \"Region\", \"Industry\").","example":"Priority Level"},"description":{"type":"string","description":"Optional description providing context about what information should be entered in this field. Helps users understand the purpose of the field.","example":"Priority level for the organization"},"type":{"type":"string","description":"Field type enum value that determines what kind of input is used. Valid values: Text (single-line), Textarea (multi-line), Number, Date, Checkbox, Select (single choice), MultiSelect (multiple choices).","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"selectOptions":{"description":"Array of select options. REQUIRED for Select and MultiSelect field types. Each option contains a value and translations. Not used for other field types.","type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldSelectOptionDto"}},"translations":{"description":"Array of translations for the field name. Each translation contains a language code and translated name. The system automatically displays the correct language based on user preferences.","example":[{"language":"en","name":"Priority Level"},{"language":"de","name":"Prioritätsstufe"}],"type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldTranslationDto"}},"translationsDescriptions":{"description":"Optional array of translations for the field description. Each translation contains a language code and translated description. Used for multilingual help text.","type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldTranslationDto"}}},"required":["name","type","translations"]},"UpdateOrganizationCustomFieldDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the custom field","example":"Priority Level"},"description":{"type":"string","description":"Description of the custom field","example":"Priority level for the organization"},"type":{"type":"string","description":"Type of the custom field","enum":["Text","Textarea","Number","Date","Checkbox","Select","MultiSelect","Currency","Url"],"example":"Select"},"selectOptions":{"description":"Select options (required for Select type fields)","type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldSelectOptionDto"}},"translations":{"description":"Translations for the custom field name","example":[{"language":"en","name":"Priority Level"},{"language":"de","name":"Prioritätsstufe"}],"type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldTranslationDto"}},"translationsDescriptions":{"description":"Translations for the custom field description","type":"array","items":{"$ref":"#/components/schemas/OrganizationCustomFieldTranslationDto"}}},"required":["name","type","translations"]},"SortOrganizationCustomFieldsDto":{"type":"object","properties":{"ids":{"description":"Array of custom field IDs in the desired order","example":["field-1","field-2","field-3"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"OrganizationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Organization ID","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"Organization name","example":"Acme Corporation"},"icon":{"type":"object","description":"Icon identifier","example":"building"},"shortName":{"type":"object","description":"Short identifier","example":"ACME"},"legalForm":{"type":"object","description":"Legal form","example":"GmbH"},"type":{"type":"string","description":"Organization type","enum":["Customer","Prospect","Target","Partner","Supplier","ServiceProvider","Investor","Competitor","GovernmentAgency","EducationalInstitution"],"example":"Customer"},"color":{"type":"string","description":"Organization color (hex code)","example":"#FF5733"},"isActive":{"type":"boolean","description":"Whether organization is active","example":true},"startOfCollaboration":{"type":"string","description":"Start of collaboration date","format":"date","example":"2024-01-01"},"addressStreet":{"type":"object","description":"Street address","example":"123 Main St"},"addressZip":{"type":"object","description":"ZIP/Postal code","example":"10001"},"addressCity":{"type":"object","description":"City","example":"New York"},"addressCountry":{"type":"object","description":"Country","example":"USA"},"addressHouseNumber":{"type":"object","description":"House number","example":"42A"},"taxNumber":{"type":"object","description":"Tax identification number","example":"DE123456789"},"registrationNumber":{"type":"object","description":"Registration number","example":"HRB 12345"},"registrationCourt":{"type":"object","description":"Registration court","example":"Munich District Court"},"shortDescription":{"type":"object","description":"Short description","example":"Leading software development company"},"phoneNumber":{"type":"object","description":"Phone number","example":"+1234567890"},"faxNumber":{"type":"object","description":"Fax number","example":"+1234567891"},"email":{"type":"object","description":"Email address","example":"contact@acme.com"},"website":{"type":"object","description":"Website URL","example":"https://acme.com"},"logoUrl":{"type":"object","description":"Logo URL (publicly accessible)","example":"https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000"},"hourRate":{"type":"object","description":"Hourly rate","example":150},"invoiceDueDays":{"type":"object","description":"Invoice due days","example":30},"enableInvoiceReminderFee":{"type":"object","description":"Enable invoice reminder fee","example":true},"invoiceReminderFee":{"type":"object","description":"Invoice reminder fee amount","example":5},"enableInvoiceInterest":{"type":"object","description":"Enable invoice interest","example":false},"baseInvoiceInterest":{"type":"object","description":"Base invoice interest rate","example":3},"invoiceInterest":{"type":"object","description":"Invoice interest rate","example":5},"invoiceLanguage":{"type":"object","description":"Invoice language code","example":"en"},"projectDocumentEnableToc":{"type":"object","description":"Enable table of contents in project documents","example":true},"projectDocumentTitlePage":{"type":"object","description":"Show title page in project documents","example":true},"projectDocumentHeadingStyle":{"type":"object","description":"Heading style for project documents","example":"classic"},"projectDocumentDefaultContactUserId":{"type":"object","description":"Default contact user ID for project documents","example":"123e4567-e89b-12d3-a456-426614174000"},"createdAt":{"type":"string","description":"Creation timestamp","example":"2024-01-01T00:00:00.000Z"},"updatedAt":{"type":"string","description":"Last update timestamp","example":"2024-01-15T12:30:00.000Z"},"parentOrganizationId":{"type":"object","description":"Parent organization ID (for hierarchical organization structure)","example":"123e4567-e89b-12d3-a456-426614174000"},"parentOrganizationName":{"type":"object","description":"Parent organization name","example":"B. Braun"},"customFields":{"type":"object","description":"Custom field values as key-value pairs. Keys match custom field IDs configured in workspace settings. Values match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields.","additionalProperties":true,"example":{"customField1":"value1","customField2":123}},"tags":{"description":"Array of tag UUIDs assigned to this organization. Tags enable cross-project organization and filtering.","example":["123e4567-e89b-12d3-a456-426614174000","223e4567-e89b-12d3-a456-426614174001"],"type":"array","items":{"type":"string"}}},"required":["id","name","type","color","isActive","createdAt","updatedAt"]},"CreateOrganizationDto":{"type":"object","properties":{"name":{"type":"string","description":"Full legal name of the organization (required)","example":"Acme Corporation"},"icon":{"type":"string","description":"Icon identifier for visual representation in lists and projects. Must be in the format `:icon_name:` (e.g., `:building:`, `:office:`, `:factory:`). Use standard emoji short names or custom icon names created in the workspace.","example":":building:"},"shortName":{"type":"string","description":"Short abbreviation for the organization (3-5 characters, alphanumeric only). Used in projects and lists for quick identification. If not provided, will be auto-generated from the organization name.","example":"ACME","maxLength":5},"legalForm":{"type":"string","description":"Legal company form or structure (e.g., \"GmbH\", \"AG\", \"LLC\", \"Inc.\", \"Ltd.\")","example":"GmbH"},"type":{"type":"string","description":"Type of organization defining the business relationship. Options: Customer (existing business partner), Prospect (potential customer), Supplier (external provider), Partner (strategic partner), ServiceProvider, Investor, Competitor, GovernmentAgency, EducationalInstitution, Target (target customer group).","enum":["Customer","Prospect","Target","Partner","Supplier","ServiceProvider","Investor","Competitor","GovernmentAgency","EducationalInstitution"],"example":"Customer"},"color":{"type":"string","description":"Hex color code used to visually distinguish this organization in lists, projects, and throughout the application (required)","example":"#FF5733"},"startOfCollaboration":{"type":"string","description":"Date when the business relationship with this organization started. Format: ISO 8601 date (YYYY-MM-DD).","format":"date","example":"2024-01-01"},"addressStreet":{"type":"string","description":"Street address","example":"123 Main St"},"addressZip":{"type":"string","description":"ZIP/Postal code","example":"10001"},"addressCity":{"type":"string","description":"City","example":"New York"},"addressCountry":{"type":"string","description":"Country","example":"USA"},"addressHouseNumber":{"type":"string","description":"House number","example":"42A"},"taxNumber":{"type":"string","description":"Tax identification number","example":"DE123456789"},"registrationNumber":{"type":"string","description":"Registration number","example":"HRB 12345"},"registrationCourt":{"type":"string","description":"Registration court","example":"Munich District Court"},"shortDescription":{"type":"string","description":"Short description of the organization","example":"Leading software development company"},"phoneNumber":{"type":"string","description":"Phone number","example":"+1234567890"},"faxNumber":{"type":"string","description":"Fax number","example":"+1234567891"},"email":{"type":"string","description":"Email address","example":"contact@acme.com"},"website":{"type":"string","description":"Website URL","example":"https://acme.com"},"logoId":{"type":"string","description":"UUID of the uploaded logo file. To upload a logo: 1) Call POST /api/public/workspace/upload to upload the file, 2) Use the returned file ID here. The logo will appear in organization lists and project views.","example":"123e4567-e89b-12d3-a456-426614174000"},"hourRate":{"type":"number","description":"Customer-specific hourly rate used for all billable time entries for this organization. Overrides workspace default. Used when creating invoices.","example":150},"invoiceDueDays":{"type":"number","description":"Number of days after invoice date until payment is due. Valid range: 1-120. Overrides workspace default for this organization.","example":30},"enableInvoiceReminderFee":{"type":"boolean","description":"Whether to charge a reminder fee for overdue invoices. Overrides workspace default. Set to true to enable, false to disable.","example":true},"invoiceReminderFee":{"type":"number","description":"Amount charged as a reminder fee for overdue invoices. Valid range: 0-1000. Only applies if enableInvoiceReminderFee is true.","example":5},"enableInvoiceInterest":{"type":"boolean","description":"Whether to charge interest on overdue invoices. Overrides workspace default. Set to true to enable, false to disable.","example":false},"baseInvoiceInterest":{"type":"number","description":"Base interest rate percentage (0-1000) used as the foundation for calculating default interest. Typically set by central banks (e.g., ECB, Bundesbank) and changes every six months. The final interest rate is baseInvoiceInterest + invoiceInterest.","example":3},"invoiceInterest":{"type":"number","description":"Interest rate percentage (0-1000) added to baseInvoiceInterest to calculate the total default interest rate. According to BGB §288: typically +5% for private customers, +9% for businesses. Final rate = baseInvoiceInterest + invoiceInterest.","example":5},"invoiceLanguage":{"type":"string","description":"Language code used for generating invoices and reminders for this organization (e.g., \"en\", \"de\", \"fr\"). Overrides workspace default.","example":"en"},"projectDocumentEnableToc":{"type":"boolean","description":"Whether to automatically include a table of contents in project documents (quotes, requirement documents, contracts) for this organization. Overrides workspace default.","example":true},"projectDocumentTitlePage":{"type":"boolean","description":"Whether to include a title/cover page in project documents for this organization. Overrides workspace default.","example":true},"projectDocumentHeadingStyle":{"type":"string","description":"Formatting style for headings in project documents (e.g., \"Sequential\", \"Normal\"). Controls the hierarchy and appearance of document headings. Overrides workspace default.","example":"classic"},"projectDocumentDefaultContactUserId":{"type":"string","description":"UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents (quotes, order confirmations, etc.) created for this organization.","example":"123e4567-e89b-12d3-a456-426614174000"},"createProject":{"type":"boolean","description":"Create a default project for this organization (internal use only)","example":false},"parentOrganizationId":{"type":"string","description":"UUID of the parent organization. Only top-level organizations (without a parent) can be selected as parents. Enables one-level hierarchical organization structure.","example":"123e4567-e89b-12d3-a456-426614174000"},"customFields":{"type":"object","description":"Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields and their configurations.","additionalProperties":true,"example":{"customField1":"value1","customField2":123}},"tags":{"description":"Array of tag UUIDs to categorize this organization. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.","example":["123e4567-e89b-12d3-a456-426614174000","223e4567-e89b-12d3-a456-426614174001"],"type":"array","items":{"type":"string"}}},"required":["name","type","color"]},"UpdateOrganizationDto":{"type":"object","properties":{"name":{"type":"string","description":"Full legal name of the organization (required)","example":"Acme Corporation"},"icon":{"type":"string","description":"Icon identifier for visual representation in lists and projects. Must be in the format `:icon_name:` (e.g., `:building:`, `:office:`, `:factory:`). Use standard emoji short names or custom icon names created in the workspace.","example":":building:"},"shortName":{"type":"string","description":"Short abbreviation for the organization (3-5 characters, alphanumeric only). Used in projects and lists for quick identification. If not provided, will be auto-generated from the organization name.","example":"ACME","maxLength":5},"legalForm":{"type":"string","description":"Legal company form or structure (e.g., \"GmbH\", \"AG\", \"LLC\", \"Inc.\", \"Ltd.\")","example":"GmbH"},"type":{"type":"string","description":"Type of organization defining the business relationship. Options: Customer (existing business partner), Prospect (potential customer), Supplier (external provider), Partner (strategic partner), ServiceProvider, Investor, Competitor, GovernmentAgency, EducationalInstitution, Target (target customer group).","enum":["Customer","Prospect","Target","Partner","Supplier","ServiceProvider","Investor","Competitor","GovernmentAgency","EducationalInstitution"],"example":"Customer"},"color":{"type":"string","description":"Hex color code used to visually distinguish this organization in lists, projects, and throughout the application (required)","example":"#FF5733"},"startOfCollaboration":{"type":"string","description":"Date when the business relationship with this organization started. Format: ISO 8601 date (YYYY-MM-DD).","format":"date","example":"2024-01-01"},"addressStreet":{"type":"string","description":"Street address","example":"123 Main St"},"addressZip":{"type":"string","description":"ZIP/Postal code","example":"10001"},"addressCity":{"type":"string","description":"City","example":"New York"},"addressCountry":{"type":"string","description":"Country","example":"USA"},"addressHouseNumber":{"type":"string","description":"House number","example":"42A"},"taxNumber":{"type":"string","description":"Tax identification number","example":"DE123456789"},"registrationNumber":{"type":"string","description":"Registration number","example":"HRB 12345"},"registrationCourt":{"type":"string","description":"Registration court","example":"Munich District Court"},"shortDescription":{"type":"string","description":"Short description of the organization","example":"Leading software development company"},"phoneNumber":{"type":"string","description":"Phone number","example":"+1234567890"},"faxNumber":{"type":"string","description":"Fax number","example":"+1234567891"},"email":{"type":"string","description":"Email address","example":"contact@acme.com"},"website":{"type":"string","description":"Website URL","example":"https://acme.com"},"logoId":{"type":"string","description":"UUID of the uploaded logo file. To upload a logo: 1) Call POST /api/public/workspace/upload to upload the file, 2) Use the returned file ID here. The logo will appear in organization lists and project views.","example":"123e4567-e89b-12d3-a456-426614174000"},"hourRate":{"type":"number","description":"Customer-specific hourly rate used for all billable time entries for this organization. Overrides workspace default. Used when creating invoices.","example":150},"invoiceDueDays":{"type":"number","description":"Number of days after invoice date until payment is due. Valid range: 1-120. Overrides workspace default for this organization.","example":30},"enableInvoiceReminderFee":{"type":"boolean","description":"Whether to charge a reminder fee for overdue invoices. Overrides workspace default. Set to true to enable, false to disable.","example":true},"invoiceReminderFee":{"type":"number","description":"Amount charged as a reminder fee for overdue invoices. Valid range: 0-1000. Only applies if enableInvoiceReminderFee is true.","example":5},"enableInvoiceInterest":{"type":"boolean","description":"Whether to charge interest on overdue invoices. Overrides workspace default. Set to true to enable, false to disable.","example":false},"baseInvoiceInterest":{"type":"number","description":"Base interest rate percentage (0-1000) used as the foundation for calculating default interest. Typically set by central banks (e.g., ECB, Bundesbank) and changes every six months. The final interest rate is baseInvoiceInterest + invoiceInterest.","example":3},"invoiceInterest":{"type":"number","description":"Interest rate percentage (0-1000) added to baseInvoiceInterest to calculate the total default interest rate. According to BGB §288: typically +5% for private customers, +9% for businesses. Final rate = baseInvoiceInterest + invoiceInterest.","example":5},"invoiceLanguage":{"type":"string","description":"Language code used for generating invoices and reminders for this organization (e.g., \"en\", \"de\", \"fr\"). Overrides workspace default.","example":"en"},"projectDocumentEnableToc":{"type":"boolean","description":"Whether to automatically include a table of contents in project documents (quotes, requirement documents, contracts) for this organization. Overrides workspace default.","example":true},"projectDocumentTitlePage":{"type":"boolean","description":"Whether to include a title/cover page in project documents for this organization. Overrides workspace default.","example":true},"projectDocumentHeadingStyle":{"type":"string","description":"Formatting style for headings in project documents (e.g., \"Sequential\", \"Normal\"). Controls the hierarchy and appearance of document headings. Overrides workspace default.","example":"classic"},"projectDocumentDefaultContactUserId":{"type":"string","description":"UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents (quotes, order confirmations, etc.) created for this organization.","example":"123e4567-e89b-12d3-a456-426614174000"},"createProject":{"type":"boolean","description":"Create a default project for this organization (internal use only)","example":false},"parentOrganizationId":{"type":"string","description":"UUID of the parent organization. Only top-level organizations (without a parent) can be selected as parents. Enables one-level hierarchical organization structure.","example":"123e4567-e89b-12d3-a456-426614174000"},"customFields":{"type":"object","description":"Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields and their configurations.","additionalProperties":true,"example":{"customField1":"value1","customField2":123}},"tags":{"description":"Array of tag UUIDs to categorize this organization. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.","example":["123e4567-e89b-12d3-a456-426614174000","223e4567-e89b-12d3-a456-426614174001"],"type":"array","items":{"type":"string"}}},"required":["name","type","color"]},"OrganizationOfferTextsDto":{"type":"object","properties":{"offerIntroText":{"type":"object","description":"Custom intro text for offers (HTML or Markdown). Overrides workspace default. Set to null to clear.","nullable":true},"offerOutroText":{"type":"object","description":"Custom outro text for offers (HTML or Markdown). Overrides workspace default. Set to null to clear.","nullable":true}}},"GetOrganizationDocumentSettingsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Organization ID","example":"123e4567-e89b-12d3-a456-426614174000"},"projectDocumentEnableToc":{"type":"object","description":"Whether to automatically include a table of contents in project documents for this organization. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable.","example":true,"nullable":true},"projectDocumentTitlePage":{"type":"object","description":"Whether to include a title/cover page in project documents for this organization. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable.","example":false,"nullable":true},"projectDocumentHeadingStyle":{"type":"string","description":"Formatting style for headings in project documents (e.g., Sequential, Normal). Controls the hierarchy and appearance of document headings. Set to `null` to inherit the workspace default, or provide a specific style to override.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential","nullable":true},"projectDocumentDefaultContactUserId":{"type":"object","description":"UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents created for this organization. Set to `null` to clear the default contact.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"projectDocumentEnableTocDefault":{"type":"boolean","description":"Workspace default setting for enabling table of contents in project documents. This value is used when the organization-specific setting is `null`.","example":true},"projectDocumentTitlePageDefault":{"type":"boolean","description":"Workspace default setting for showing title/cover page in project documents. This value is used when the organization-specific setting is `null`.","example":true},"projectDocumentHeadingStyleDefault":{"type":"string","description":"Workspace default heading style for project documents. This value is used when the organization-specific setting is `null`.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Normal"},"documentLanguage":{"type":"object","description":"Document language override for this organization. Overrides workspace default. Null if not set (inherits workspace default).","example":"de","nullable":true},"documentLanguageDefault":{"type":"string","description":"Workspace default document language. This value is used when the organization-specific setting is `null`.","example":"en"},"offerTexts":{"type":"object","description":"Offer text overrides for this organization (returned as HTML). Overrides workspace defaults. Null if not set (inherits workspace defaults).","nullable":true},"offerTextsWorkspaceDefault":{"description":"Workspace default offer texts (returned as HTML). These are used when organization-specific texts are not set.","allOf":[{"$ref":"#/components/schemas/OrganizationOfferTextsDto"}]}},"required":["id","projectDocumentEnableToc","projectDocumentTitlePage","projectDocumentHeadingStyle","projectDocumentDefaultContactUserId","projectDocumentEnableTocDefault","projectDocumentTitlePageDefault","projectDocumentHeadingStyleDefault","documentLanguage","documentLanguageDefault","offerTexts","offerTextsWorkspaceDefault"]},"PatchOrganizationDocumentSettingsDto":{"type":"object","properties":{"projectDocumentEnableToc":{"type":"object","description":"Whether to automatically include a table of contents in project documents. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable. Omit this field to leave it unchanged.","example":true,"nullable":true},"projectDocumentTitlePage":{"type":"object","description":"Whether to include a title/cover page in project documents. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable. Omit this field to leave it unchanged.","example":false,"nullable":true},"projectDocumentHeadingStyle":{"type":"string","description":"Formatting style for headings in project documents (e.g., Sequential, Normal). Set to `null` to inherit the workspace default, or provide a specific style to override. Omit this field to leave it unchanged.","enum":["Normal","Sequential","SequentialFromSecondLevel","SequentialWithParagraphs"],"example":"Sequential","nullable":true},"projectDocumentDefaultContactUserId":{"type":"object","description":"UUID of the organization member (contact person) to set as the default recipient for new project documents. Set to `null` to clear the default contact. Omit this field to leave it unchanged.","example":"123e4567-e89b-12d3-a456-426614174000","nullable":true},"documentLanguage":{"type":"object","description":"Document language override for this organization. Overrides workspace default. Set to null to inherit workspace default. Omit this field to leave it unchanged.","example":"de"},"offerTexts":{"description":"Offer text overrides for this organization. Overrides workspace defaults. Set to null to inherit workspace defaults. Omit this field to leave it unchanged.","allOf":[{"$ref":"#/components/schemas/OrganizationOfferTextsDto"}]}}},"PatchOrganizationDto":{"type":"object","properties":{"name":{"type":"string","description":"Full legal name of the organization (required)","example":"Acme Corporation"},"icon":{"type":"string","description":"Icon identifier for visual representation in lists and projects. Must be in the format `:icon_name:` (e.g., `:building:`, `:office:`, `:factory:`). Use standard emoji short names or custom icon names created in the workspace.","example":":building:"},"shortName":{"type":"string","description":"Short abbreviation for the organization (3-5 characters, alphanumeric only). Used in projects and lists for quick identification. If not provided, will be auto-generated from the organization name.","example":"ACME","maxLength":5},"legalForm":{"type":"string","description":"Legal company form or structure (e.g., \"GmbH\", \"AG\", \"LLC\", \"Inc.\", \"Ltd.\")","example":"GmbH"},"type":{"type":"string","description":"Type of organization defining the business relationship. Options: Customer (existing business partner), Prospect (potential customer), Supplier (external provider), Partner (strategic partner), ServiceProvider, Investor, Competitor, GovernmentAgency, EducationalInstitution, Target (target customer group).","enum":["Customer","Prospect","Target","Partner","Supplier","ServiceProvider","Investor","Competitor","GovernmentAgency","EducationalInstitution"],"example":"Customer"},"color":{"type":"string","description":"Hex color code used to visually distinguish this organization in lists, projects, and throughout the application (required)","example":"#FF5733"},"startOfCollaboration":{"type":"string","description":"Date when the business relationship with this organization started. Format: ISO 8601 date (YYYY-MM-DD).","format":"date","example":"2024-01-01"},"addressStreet":{"type":"string","description":"Street address","example":"123 Main St"},"addressZip":{"type":"string","description":"ZIP/Postal code","example":"10001"},"addressCity":{"type":"string","description":"City","example":"New York"},"addressCountry":{"type":"string","description":"Country","example":"USA"},"addressHouseNumber":{"type":"string","description":"House number","example":"42A"},"taxNumber":{"type":"string","description":"Tax identification number","example":"DE123456789"},"registrationNumber":{"type":"string","description":"Registration number","example":"HRB 12345"},"registrationCourt":{"type":"string","description":"Registration court","example":"Munich District Court"},"shortDescription":{"type":"string","description":"Short description of the organization","example":"Leading software development company"},"phoneNumber":{"type":"string","description":"Phone number","example":"+1234567890"},"faxNumber":{"type":"string","description":"Fax number","example":"+1234567891"},"email":{"type":"string","description":"Email address","example":"contact@acme.com"},"website":{"type":"string","description":"Website URL","example":"https://acme.com"},"logoId":{"type":"string","description":"UUID of the uploaded logo file. To upload a logo: 1) Call POST /api/public/workspace/upload to upload the file, 2) Use the returned file ID here. The logo will appear in organization lists and project views.","example":"123e4567-e89b-12d3-a456-426614174000"},"hourRate":{"type":"number","description":"Customer-specific hourly rate used for all billable time entries for this organization. Overrides workspace default. Used when creating invoices.","example":150},"invoiceDueDays":{"type":"number","description":"Number of days after invoice date until payment is due. Valid range: 1-120. Overrides workspace default for this organization.","example":30},"enableInvoiceReminderFee":{"type":"boolean","description":"Whether to charge a reminder fee for overdue invoices. Overrides workspace default. Set to true to enable, false to disable.","example":true},"invoiceReminderFee":{"type":"number","description":"Amount charged as a reminder fee for overdue invoices. Valid range: 0-1000. Only applies if enableInvoiceReminderFee is true.","example":5},"enableInvoiceInterest":{"type":"boolean","description":"Whether to charge interest on overdue invoices. Overrides workspace default. Set to true to enable, false to disable.","example":false},"baseInvoiceInterest":{"type":"number","description":"Base interest rate percentage (0-1000) used as the foundation for calculating default interest. Typically set by central banks (e.g., ECB, Bundesbank) and changes every six months. The final interest rate is baseInvoiceInterest + invoiceInterest.","example":3},"invoiceInterest":{"type":"number","description":"Interest rate percentage (0-1000) added to baseInvoiceInterest to calculate the total default interest rate. According to BGB §288: typically +5% for private customers, +9% for businesses. Final rate = baseInvoiceInterest + invoiceInterest.","example":5},"invoiceLanguage":{"type":"string","description":"Language code used for generating invoices and reminders for this organization (e.g., \"en\", \"de\", \"fr\"). Overrides workspace default.","example":"en"},"projectDocumentEnableToc":{"type":"boolean","description":"Whether to automatically include a table of contents in project documents (quotes, requirement documents, contracts) for this organization. Overrides workspace default.","example":true},"projectDocumentTitlePage":{"type":"boolean","description":"Whether to include a title/cover page in project documents for this organization. Overrides workspace default.","example":true},"projectDocumentHeadingStyle":{"type":"string","description":"Formatting style for headings in project documents (e.g., \"Sequential\", \"Normal\"). Controls the hierarchy and appearance of document headings. Overrides workspace default.","example":"classic"},"projectDocumentDefaultContactUserId":{"type":"string","description":"UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents (quotes, order confirmations, etc.) created for this organization.","example":"123e4567-e89b-12d3-a456-426614174000"},"createProject":{"type":"boolean","description":"Create a default project for this organization (internal use only)","example":false},"parentOrganizationId":{"type":"string","description":"UUID of the parent organization. Only top-level organizations (without a parent) can be selected as parents. Enables one-level hierarchical organization structure.","example":"123e4567-e89b-12d3-a456-426614174000"},"customFields":{"type":"object","description":"Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields and their configurations.","additionalProperties":true,"example":{"customField1":"value1","customField2":123}},"tags":{"description":"Array of tag UUIDs to categorize this organization. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.","example":["123e4567-e89b-12d3-a456-426614174000","223e4567-e89b-12d3-a456-426614174001"],"type":"array","items":{"type":"string"}}}},"OrganizationInvoiceSettingsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Organization ID","example":"uuid-123"},"hourRate":{"type":"object","description":"Hourly rate","example":75,"nullable":true},"invoiceDueDays":{"type":"object","description":"Invoice due days","example":30,"nullable":true},"enableInvoiceReminderFee":{"type":"object","description":"Enable invoice reminder fee","example":true,"nullable":true},"invoiceReminderFee":{"type":"object","description":"Invoice reminder fee","example":5.5,"nullable":true},"enableInvoiceInterest":{"type":"object","description":"Enable invoice interest","example":true,"nullable":true},"baseInvoiceInterest":{"type":"object","description":"Base invoice interest","example":2.5,"nullable":true},"invoiceInterest":{"type":"object","description":"Invoice interest","example":8,"nullable":true},"invoiceLanguage":{"type":"object","description":"Invoice language","example":"en","nullable":true},"customTexts":{"type":"object","description":"Custom invoice text templates (returned as HTML)","properties":{"withBestRegards":{"type":"string","nullable":true},"invoiceGreeting":{"type":"string","nullable":true},"invoiceFooter":{"type":"string","nullable":true},"invoiceReminderFeeWarning":{"type":"string","nullable":true},"firstReminderGreeting":{"type":"string","nullable":true},"firstReminderFooter":{"type":"string","nullable":true},"secondReminderGreeting":{"type":"string","nullable":true},"secondReminderFooter":{"type":"string","nullable":true},"cancelationBody":{"type":"string","nullable":true}}}},"required":["id","hourRate","invoiceDueDays","enableInvoiceReminderFee","invoiceReminderFee","enableInvoiceInterest","baseInvoiceInterest","invoiceInterest","invoiceLanguage","customTexts"]},"PatchOrganizationInvoiceSettingsDto":{"type":"object","properties":{"hourRate":{"type":"object","description":"Hourly rate for the organization","example":75,"nullable":true},"invoiceDueDays":{"type":"object","description":"Number of days until invoice is due (1-120)","example":30,"nullable":true},"enableInvoiceReminderFee":{"type":"object","description":"Enable invoice reminder fee (null = use default)","example":true,"nullable":true},"invoiceReminderFee":{"type":"object","description":"Invoice reminder fee amount (0-1000)","example":5.5,"nullable":true},"enableInvoiceInterest":{"type":"object","description":"Enable invoice interest (null = use default)","example":true,"nullable":true},"baseInvoiceInterest":{"type":"object","description":"Base invoice interest rate percentage (0-1000)","example":2.5,"nullable":true},"invoiceInterest":{"type":"object","description":"Invoice interest rate percentage (0-1000)","example":8,"nullable":true},"invoiceLanguage":{"type":"object","description":"Invoice language code","example":"en","nullable":true},"customTexts":{"description":"Custom invoice text templates. Content can be provided as HTML or Markdown and will be converted to internal format.","allOf":[{"$ref":"#/components/schemas/InvoiceCustomTextsDto"}]}}},"TeamUserDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the user (UUID format). Use this ID when assigning users to teams or referencing team members.","example":"123e4567-e89b-12d3-a456-426614174000"},"firstName":{"type":"string","description":"The user first name as stored in their employee profile.","example":"John"},"lastName":{"type":"string","description":"The user last name as stored in their employee profile.","example":"Doe"},"avatarId":{"type":"object","description":"Unique identifier for the user avatar image file (UUID format). This ID can be used to retrieve the avatar image through the file API. Null if the user has not uploaded an avatar.","example":"123e4567-e89b-12d3-a456-426614174001","nullable":true}},"required":["id","firstName","lastName","avatarId"]},"TeamResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the team (UUID format). Use this ID to reference the team in update, delete, or project assignment operations.","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"The display name of the team. This name appears in team lists, project assignments, and throughout the application.","example":"Development Team"},"icon":{"type":"string","description":"Emoji icon used for visual identification of the team. This icon appears next to the team name in lists and can be used to quickly identify teams. Common examples include: \"🧑‍🤝‍🧑\" (people holding hands), \"💻\" (laptop), \"🎨\" (artist palette).","example":"🧑‍🤝‍🧑"},"usersCount":{"type":"number","description":"Total number of users currently assigned to the team. This count matches the length of the users array and is provided for convenience.","example":5},"users":{"description":"Complete list of all users who are members of this team. Each user object includes their ID, name, and avatar information. Use the user IDs from this array when you need to reference team members.","type":"array","items":{"$ref":"#/components/schemas/TeamUserDto"}}},"required":["id","name","icon","usersCount","users"]},"CreateTeamDto":{"type":"object","properties":{"name":{"type":"string","description":"The display name for the team. Choose a descriptive name that clearly identifies the team purpose or department. Examples: \"Development Team\", \"Marketing\", \"Customer Support\", \"Sales Team\". This name will appear in team lists and project assignments.","example":"Development Team"},"icon":{"type":"string","description":"Emoji icon for visual identification of the team. This icon appears next to the team name throughout the application. Choose any emoji that represents the team (e.g., \"🧑‍🤝‍🧑\" for teams, \"💻\" for development, \"🎨\" for design). The icon must be a non-empty string.","example":"🧑‍🤝‍🧑"},"users":{"description":"Array of user IDs (UUIDs) to assign as initial team members. At least one user must be provided. All user IDs must be valid UUIDs of existing employees in the workspace. You can add or remove members later using the update endpoint.","example":["123e4567-e89b-12d3-a456-426614174000","123e4567-e89b-12d3-a456-426614174001"],"type":"array","items":{"type":"string"}}},"required":["name","icon","users"]},"PatchTeamDto":{"type":"object","properties":{"name":{"type":"string","description":"New display name for the team. If provided, replaces the existing team name. If omitted, the team name remains unchanged.","example":"Updated Team Name"},"icon":{"type":"string","description":"New emoji icon for the team. If provided, replaces the existing team icon. If omitted, the team icon remains unchanged.","example":"💻"},"users":{"description":"Complete array of user IDs (UUIDs) that should be members of the team. If provided, this array replaces the entire team membership (it is not merged with existing members). You must include all users you want in the team, including existing members you want to keep. At least one user must be included. If omitted, the team membership remains unchanged. All user IDs must be valid UUIDs of existing employees in the workspace.","example":["123e4567-e89b-12d3-a456-426614174000","123e4567-e89b-12d3-a456-426614174002"],"type":"array","items":{"type":"string"}}}},"DeclineVacationDto":{"type":"object","properties":{"reason":{"type":"string","description":"Optional reason explaining why the vacation request was declined or cancelled. This reason will be stored with the vacation record and visible in the vacation history. Providing a reason helps employees understand the decision and improves transparency in the approval process.","example":"Insufficient coverage during this period"}}},"EmployeeJournalResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (UUID) for this journal entry","example":"550e8400-e29b-41d4-a716-446655440000"},"employeeId":{"type":"string","description":"UUID of the employee this journal entry is associated with. This identifies which employee the entry is about.","example":"550e8400-e29b-41d4-a716-446655440000"},"createdAt":{"type":"string","description":"ISO 8601 timestamp indicating when this journal entry was created. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., \"2024-01-15T10:30:00Z\").","example":"2024-01-15T10:30:00Z"},"createdBy":{"type":"string","description":"UUID of the user who created this journal entry. This identifies who wrote the entry, which is useful for tracking authorship and accountability.","example":"550e8400-e29b-41d4-a716-446655440000"},"lastUpdated":{"type":"string","description":"ISO 8601 timestamp indicating when this journal entry was last modified. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., \"2024-01-16T14:20:00Z\"). This is automatically updated whenever the entry is edited via the PATCH endpoint.","example":"2024-01-16T14:20:00Z"},"mood":{"type":"string","description":"Emotional indicator for the journal entry. Values: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). This helps categorize and filter entries by emotional context.","enum":["Sad","Neutral","Happy"],"example":"Neutral"},"reminder":{"type":"object","description":"Optional reminder date in ISO 8601 format (YYYY-MM-DD). Set when creating or updating the entry to track follow-up dates for goals, agreements, or actions. Returns `null` if no reminder is set. Example: \"2024-12-31\" for December 31, 2024.","example":"2024-12-31","nullable":true},"body":{"type":"string","description":"Journal entry content in HTML format. The content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications. Contains the rich text content including headings, paragraphs, lists, links, and formatting as originally provided.","example":"<h1>Employee Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>"}},"required":["id","employeeId","createdAt","createdBy","lastUpdated","mood","reminder","body"]},"CreateEmployeeJournalDto":{"type":"object","properties":{"employeeId":{"type":"string","description":"UUID of the employee this journal entry is about. The employee must exist in the workspace and be accessible to the current user. Employees can only create entries for their own employee record unless they have the `Employees.edit` permission.","example":"550e8400-e29b-41d4-a716-446655440000"},"body":{"type":"string","description":"Rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n Use this field to document conversations, observations, agreements, or any notes about the employee.","example":"<h1>Employee Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>"},"mood":{"type":"string","description":"Emotional indicator for the journal entry. Use this to quickly categorize the tone or sentiment of the interaction. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). Defaults to `Neutral` if not provided. This helps filter and analyze journal entries by emotional context.","enum":["Sad","Neutral","Happy"],"example":"Neutral"},"reminder":{"type":"string","description":"Optional reminder date in ISO 8601 format (YYYY-MM-DD). Use this to set a follow-up date for reviewing goals, agreements, or taking action related to this journal entry. The reminder date can be used to filter entries and trigger notifications. Example: \"2024-12-31\" for December 31, 2024. If not provided, no reminder is set.","example":"2024-12-31"}},"required":["employeeId","body"]},"PatchEmployeeJournalDto":{"type":"object","properties":{"employeeId":{"type":"string","description":"UUID of the employee to associate this journal entry with. If provided, changes which employee this entry is about. The new employee must exist and be accessible. If the new employee is the current user's own employee record, `visibleToSubject` will be automatically set to `true`. If omitted, the existing employee association is preserved.","example":"550e8400-e29b-41d4-a716-446655440000"},"body":{"type":"string","description":"Updated rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.\n\nFor quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.\n\nUse only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">link text</a>.\n\nIn HTML you can use:\n## Node Types and Marks\n\n### Nodes\n\n**paragraph**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nDefault paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.\nAtts:\n- extraPlaceholder: null\n```html\n<p class=\"paragraph-base\" extraplaceholder=\"null/some-value\"></p>\n```\n\n**heading**\nType: block\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nAtts:\n- level: 1\n```html\n<h1 class=\"heading-base\"></h1>\n```\n\n**bulletList**\nType: block list\nContent: listItem+\nAtts: none\n```html\n<ul class=\"list-base\"><li></li></ul>\n```\n\n**hardBreak**\nType: inline\nAtts: none\n```html\n<br>\n```\n\n**horizontalRule**\nType: block\nAtts: none\n```html\n<hr class=\"hr-base\">\n```\n\n**orderedList**\nType: block list\nContent: listItem+\nAtts:\n- start: 1\n- type: null\n```html\n<ol class=\"order-list-base\" type=\"null/some-value\"><li></li></ol>\n```\n\n**listItem**\nContent: (paragraph|list)* (paragraphs and/or lists, in any order)\nAtts: none\n```html\n<li></li>\n```\n\n**blockquote**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts: none\n```html\n<blockquote class=\"blockquote-base\"><p class=\"paragraph-base\"></p></blockquote>\n```\n\n**table**\nType: block\nContent: tableRow+\nAtts: none\n```html\n<table style=\"width: 0px;\"><colgroup></colgroup><tbody><tr></tr></tbody></table>\n```\n\n**tableRow**\nContent: (tableCell | tableHeader)*\nAtts: none\n```html\n<tr></tr>\n```\n\n**tableHeader**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<th colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></th>\n```\n\n**tableCell**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nAtts:\n- colspan: 1\n- rowspan: 1\n- colwidth: null\n- align: null\n```html\n<td colspan=\"1\" rowspan=\"1\" colwidth=\"null/some-value\" style=\"text-align: null/some-value;\"><p class=\"paragraph-base\"></p></td>\n```\n\n**callout**\nType: block\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nCallout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class=\"paragraph-base\"> inside the callout <div>. data-type is \"callout\".\nAtts:\n- icon: :information_source:\n```html\n<div data-type=\"callout\" icon=\":information_source:\"><p class=\"paragraph-base\"></p></div>\n```\n\n**appImage**\nType: block\nBlock for an image file already uploaded in Leadtime.\n- fileId: Id of the stored image. Must be a real uploaded file; do not invent.\n- filename: Display name; should match the file.\n- width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.\nAtts:\n- fileId: \n- filename: \n- width: 500\n- align: left\n- size: 0\n```html\n<div data-type=\"appImage\" fileid=\"\" filename=\"\" width=\"500\" align=\"left\" size=\"0\"></div>\n```\n\n**collapse**\nType: block\nContent: collapseTitle collapseBody (a collapse title node, then a collapse body)\nExpandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.\nAtts: none\n```html\n<collapse><collapse-title></collapse-title><collapse-body><p class=\"paragraph-base\"></p></collapse-body></collapse>\n```\n\n**collapseBody**\nContent: block+ (one or more block child nodes (e.g. paragraph, list, …))\nExpandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.\nAtts: none\n```html\n<collapse-body><p class=\"paragraph-base\"></p></collapse-body>\n```\n\n**collapseTitle**\nContent: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)\nClickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.\nAtts: none\n```html\n<collapse-title></collapse-title>\n```\n\n**emoji**\nType: inline\nInline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.\nAtts:\n- icon: :smile:\n```html\n<span data-type=\"emoji\" data-icon=\":smile:\"></span>\n```\n\n### Marks\n\n**link**\nAtts:\n- href: null\n- target: _blank\n- rel: noopener noreferrer nofollow\n- class: null\n- title: null\n```html\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"\">link text example</a>\n```\n\n**bold**\nAtts: none\n```html\n<strong>bold text example</strong>\n```\n\n**code**\nAtts: none\n```html\n<code>code text example</code>\n```\n\n**italic**\nAtts: none\n```html\n<em>italic text example</em>\n```\n\n**strike**\nAtts: none\n```html\n<s>strike text example</s>\n```\n\n**underline**\nAtts: none\n```html\n<u>underline text example</u>\n```\n If omitted, the existing body content remains unchanged.","example":"<h1>Updated Employee Status</h1><p>Follow-up meeting scheduled for next week.</p>"},"mood":{"type":"string","description":"Updated emotional indicator for the journal entry. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). If omitted, the existing mood value remains unchanged.","enum":["Sad","Neutral","Happy"],"example":"Happy"},"reminder":{"type":"object","description":"Updated reminder date in ISO 8601 format (YYYY-MM-DD). Provide a date string (e.g., \"2024-12-31\") to set or update the reminder. Provide `null` to clear an existing reminder. If omitted entirely, the existing reminder remains unchanged. Use reminders to track follow-up dates for goals, agreements, or actions related to this journal entry.","example":"2024-12-31","nullable":true}}},"CalendarFeedConfigResponseDto":{"type":"object","properties":{"url":{"type":"string","description":"HTTPS URL of the employee’s Leadtime calendar export (.ics). Subscribe in external calendar apps."}},"required":["url"]},"ExternalCalendarFeedResponseDto":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"sourceUrl":{"type":"string"},"enabled":{"type":"boolean"},"lastFetchedAt":{"type":"object","nullable":true},"lastError":{"type":"object","nullable":true},"createdAt":{"type":"string"},"editedAt":{"type":"string"}},"required":["id","sourceUrl","enabled","createdAt","editedAt"]},"CreateEmployeeExternalCalendarFeedDto":{"type":"object","properties":{"label":{"type":"string"},"sourceUrl":{"type":"string","description":"HTTPS iCalendar (.ics) URL from the external provider."},"enabled":{"type":"boolean"}},"required":["sourceUrl"]},"PatchEmployeeExternalCalendarFeedDto":{"type":"object","properties":{"label":{"type":"string"},"sourceUrl":{"type":"string"},"enabled":{"type":"boolean"}}},"EmployeeResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the employee (UUID format)","example":"emp_123"},"firstName":{"type":"string","description":"First name of the employee","example":"John"},"lastName":{"type":"string","description":"Last name of the employee","example":"Doe"},"email":{"type":"string","description":"Email address of the employee. This is also the login email if canLogin is true.","example":"john.doe@company.com"},"phone":{"type":"object","description":"Phone number in the format stored in the system. Null if not provided.","example":"+1234567890","nullable":true},"position":{"type":"object","description":"Job title or position of the employee. Null if not set.","example":"Software Engineer","nullable":true},"title":{"type":"object","description":"Title prefix (e.g., \"Mr.\", \"Mrs.\", \"Dr.\", \"Prof.\"). Null if not set.","example":"Mr.","nullable":true},"degree":{"type":"object","description":"Academic degree (e.g., \"B.Sc.\", \"M.Sc.\", \"Ph.D.\"). Null if not set.","example":"M.Sc.","nullable":true},"addressStreet":{"type":"object","description":"Street name (without house number). Null if address is not set.","example":"Main Street","nullable":true},"addressZip":{"type":"object","description":"ZIP or postal code. Null if address is not set.","example":"12345","nullable":true},"addressCity":{"type":"object","description":"City name. Null if address is not set.","example":"Berlin","nullable":true},"addressCountry":{"type":"object","description":"Country code in ISO 3166-1 alpha-2 format (e.g., \"DE\", \"US\"). Null if address is not set.","example":"DE","nullable":true},"addressHouseNumber":{"type":"object","description":"House or building number. Null if address is not set.","example":"42","nullable":true},"birthDate":{"type":"object","description":"Birth date in ISO 8601 date format (YYYY-MM-DD). Null if not set.","example":"1990-01-15","nullable":true},"employmentMode":{"type":"object","description":"Employment mode indicating the type of employment (e.g., \"FullTime\", \"PartTime\", \"Contract\", \"Temporary\", \"Freelance\"). Null if not set.","example":"FullTime","nullable":true},"entryDate":{"type":"object","description":"Date when the employee joined the company, in ISO 8601 date format (YYYY-MM-DD). Null if not set.","example":"2020-01-15","nullable":true},"exitDate":{"type":"object","description":"Date when the employee left the company, in ISO 8601 date format (YYYY-MM-DD). Null if employee is still active. Used to calculate isActive status.","example":null,"nullable":true},"isActive":{"type":"boolean","description":"Whether the employee is currently active. Calculated as: exitDate is null OR exitDate is in the future. Active employees appear in employee lists and can be assigned to teams/projects.","example":true},"incomeTaxClass":{"type":"object","description":"Income tax class (typically used in German tax system, values like \"1\", \"2\", \"3\", etc.). Null if not set.","example":"1","nullable":true},"userId":{"type":"string","description":"ID of the associated user account. This links the employee record to the user account that handles authentication and permissions.","example":"user_123"},"roleId":{"type":"string","description":"Role ID assigned to the employee user account. The role determines what permissions the employee has when they log in.","example":"ws_abc_role_123"},"canLogin":{"type":"boolean","description":"Whether the employee can login to the system. If true, the employee has an active user account and can access the system.","example":true},"teamIds":{"description":"Array of team IDs the employee is assigned to. Teams are used for organizational structure and permissions. Empty array if employee is not assigned to any teams.","example":["team_1","team_2"],"type":"array","items":{"type":"string"}},"avatarUrl":{"type":"object","description":"Full URL to the employee's avatar image. Generated from the avatar file ID. Null if employee has no avatar. Use this URL to display the avatar in your application.","example":"https://workc.example.com/api/files/public/file_123","nullable":true},"createdAt":{"type":"string","description":"Timestamp when the employee record was created, in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ).","example":"2020-01-15T10:00:00Z"},"updatedAt":{"type":"string","description":"Timestamp when the employee record was last updated, in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ).","example":"2020-01-15T10:00:00Z"},"chargeableHoursDayGoal":{"type":"object","description":"Chargeable (billable) hours per day goal. Null if not set.","example":6.5,"nullable":true},"weeklyWorkingTime":{"description":"Array of weekly working time entries for the employee. Each entry defines working days and hours that apply from a specific date. The active entry for a given date is the one with the most recent dateFrom that is still on or before that date.","example":[{"id":"wwt_123","dateFrom":"2021-01-01","workingDays":[1,1,1,1,1,0,0],"weeklyWorkingHours":40}],"type":"array","items":{"type":"object"}},"salaries":{"description":"Array of salary entries for the employee. Each entry defines a salary amount that applies from a specific date.","example":[{"id":"sal_123","dateFrom":"2021-01-01","salary":5000}],"type":"array","items":{"type":"object"}},"vacationDays":{"description":"Array of vacation days entries for the employee. Each entry defines vacation days per year that apply from a specific date.","example":[{"id":"vac_123","dateFrom":"2021-01-01","days":30}],"type":"array","items":{"type":"object"}}},"required":["id","firstName","lastName","email","phone","position","title","degree","addressStreet","addressZip","addressCity","addressCountry","addressHouseNumber","birthDate","employmentMode","entryDate","exitDate","isActive","incomeTaxClass","userId","roleId","canLogin","teamIds","avatarUrl","createdAt","updatedAt","chargeableHoursDayGoal"]},"CreateEmployeeDto":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the employee. This will be synced to the user account if canLogin is true.","example":"John"},"lastName":{"type":"string","description":"Last name of the employee. This will be synced to the user account if canLogin is true.","example":"Doe"},"email":{"type":"string","description":"Email address of the employee. Must be unique within the workspace. If canLogin is true, this will be the login email. If a HelpdeskUser with this email exists, it will be upgraded to Employee type.","example":"john.doe@company.com"},"position":{"type":"string","description":"Job title or position of the employee (e.g., \"Software Engineer\", \"Product Manager\", \"Sales Director\").","example":"Software Engineer"},"roleId":{"type":"string","description":"Role ID to assign to the employee user account. The role determines what permissions the employee has when they log in. Must be a valid role ID from your workspace.","example":"ws_abc_role_123"},"canLogin":{"type":"boolean","description":"Whether the employee can login to the system. If true, a user account will be created automatically with Active status, and an invitation email will be sent. If false, no user account is created initially (but can be created later via update).","example":false,"default":false},"teams":{"description":"Array of team IDs to assign the employee to. Teams are used for organizational structure and permissions. Provide an empty array or omit to create an employee without team assignments.","example":["team_1","team_2"],"type":"array","items":{"type":"string"}},"avatarId":{"type":"string","description":"Avatar file ID obtained from POST /workspace/upload endpoint. To upload an avatar: 1) First call POST /workspace/upload with the image file, 2) Get the file ID from the response, 3) Use that ID here. Set to null to create employee without avatar (can be added later).","example":"file_123","nullable":true}},"required":["firstName","lastName","email","roleId"]},"CreateOrUpdateWeeklyWorkingTimeDto":{"type":"object","properties":{"id":{"type":"string","description":"Entry ID. If provided, updates the existing entry. If omitted, creates a new entry.","example":"wwt_123"},"dateFrom":{"type":"string","description":"Date from which this working time configuration applies, in ISO 8601 date format (YYYY-MM-DD).","example":"2021-01-01"},"workingDays":{"description":"Array of 7 numbers representing working days (Monday=0, Sunday=6). Each value is 1 if the employee works that day, 0 if not. Must have at least one working day (at least one value must be 1).","example":[1,1,1,1,1,0,0],"type":"array","items":{"type":"number"}},"weeklyWorkingHours":{"type":"number","description":"Weekly working hours (total hours per week). Maximum value is 168 (24 hours × 7 days).","example":40,"minimum":1,"maximum":168}},"required":["dateFrom","workingDays","weeklyWorkingHours"]},"CreateOrUpdateSalaryDto":{"type":"object","properties":{"id":{"type":"string","description":"Entry ID. If provided, updates the existing entry. If omitted, creates a new entry.","example":"sal_123"},"dateFrom":{"type":"string","description":"Date from which this salary applies, in ISO 8601 date format (YYYY-MM-DD).","example":"2021-01-01"},"salary":{"type":"number","description":"Monthly salary amount. Must be a positive integer.","example":5000,"minimum":0}},"required":["dateFrom","salary"]},"CreateOrUpdateVacationDaysDto":{"type":"object","properties":{"id":{"type":"string","description":"Entry ID. If provided, updates the existing entry. If omitted, creates a new entry.","example":"vac_123"},"dateFrom":{"type":"string","description":"Date from which this vacation days configuration applies, in ISO 8601 date format (YYYY-MM-DD).","example":"2021-01-01"},"days":{"type":"number","description":"Number of vacation days per year. Must be a positive integer.","example":30,"minimum":0}},"required":["dateFrom","days"]},"PatchEmployeeDto":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the employee. If changed, the associated user account will be automatically synced.","example":"Jane"},"lastName":{"type":"string","description":"Last name of the employee. If changed, the associated user account will be automatically synced.","example":"Smith"},"email":{"type":"string","description":"Email address of the employee. Must be unique within the workspace. If changed, the associated user account will be synced. If a HelpdeskUser with the new email exists, it will be upgraded to Employee type.","example":"jane.smith@company.com"},"title":{"type":"object","description":"Title prefix (e.g., \"Mr.\", \"Mrs.\", \"Ms.\", \"Dr.\", \"Prof.\"). Set to null to remove the title.","example":"Dr.","nullable":true},"degree":{"type":"object","description":"Academic degree (e.g., \"B.Sc.\", \"M.Sc.\", \"Ph.D.\", \"MBA\"). Set to null to remove the degree.","example":"M.Sc.","nullable":true},"position":{"type":"string","description":"Job title or position of the employee (e.g., \"Senior Software Engineer\", \"Product Manager\").","example":"Senior Software Engineer"},"phone":{"type":"object","description":"Phone number in any format. Set to null to remove the phone number.","example":"+1234567890","nullable":true},"addressStreet":{"type":"object","description":"Street name (without house number). Set to null to clear the street address.","example":"Main Street","nullable":true},"addressZip":{"type":"object","description":"ZIP or postal code. Set to null to clear the ZIP code.","example":"12345","nullable":true},"addressCity":{"type":"object","description":"City name. Set to null to clear the city.","example":"Berlin","nullable":true},"addressCountry":{"type":"object","description":"Country code in ISO 3166-1 alpha-2 format (e.g., \"DE\" for Germany, \"US\" for United States). Set to null to clear the country.","example":"DE","nullable":true},"addressHouseNumber":{"type":"object","description":"House or building number. Set to null to clear the house number.","example":"42","nullable":true},"birthDate":{"type":"object","description":"Birth date in ISO 8601 date format (YYYY-MM-DD). Set to null to remove the birth date.","example":"1990-01-15","nullable":true},"avatarId":{"type":"object","description":"Avatar file ID obtained from POST /workspace/upload endpoint. To update avatar: 1) Upload file via POST /workspace/upload, 2) Get file ID from response, 3) Use that ID here. Set to null to remove the avatar.","example":"file_123","nullable":true},"roleId":{"type":"string","description":"Role ID to assign to the employee user account. The role determines permissions. Must be a valid role ID from your workspace.","example":"ws_abc_role_456"},"canLogin":{"type":"boolean","description":"Whether the employee can login to the system. When changed from false to true: user account is created (if it doesn't exist), user status becomes Active, and an invitation email is sent automatically.","example":true},"teams":{"description":"Array of team IDs to assign the employee to. This replaces the entire team assignment list. Provide an empty array to remove all team assignments. Omit this field to keep existing team assignments unchanged.","example":["team_1","team_3"],"type":"array","items":{"type":"string"}},"chargeableHoursDayGoal":{"type":"object","description":"Chargeable (billable) hours per day goal. Used for time tracking targets and notifications.","example":6.5,"nullable":true}}}}},"security":[{"OAuth2":[]}]}