openapi: 3.2.0 info: title: Taskfolk Goals API version: 1.0.0 description: Public REST API for Taskfolk (taskfolk.ai). All endpoints require a workspace-scoped bearer API key. Paths are nested under `/v1/workspaces/{slug}/…`. servers: - url: https://taskfolk.ai/api security: - bearerAuth: [] tags: - name: Goals paths: /v1/workspaces/{slug}/goals: get: summary: List goals (optionally filtered by portfolio or company scope). tags: - Goals security: - bearerAuth: - goals:read parameters: - schema: type: string example: taskfolk description: Workspace slug. required: true description: Workspace slug. name: slug in: path - schema: type: string description: Only goals grouped under this portfolio. required: false description: Only goals grouped under this portfolio. name: portfolio_id in: query - schema: type: string enum: - company description: scope=company returns only company goals (no portfolio). Mutually exclusive with portfolio_id. required: false description: scope=company returns only company goals (no portfolio). Mutually exclusive with portfolio_id. name: scope in: query - schema: type: string required: false name: cursor in: query - schema: type: - integer - 'null' required: false name: limit in: query responses: '200': description: Success. content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/Goal' pagination: $ref: '#/components/schemas/Pagination' required: - data - pagination '400': description: Validation error. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing / invalid API key. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: Key lacks the required scope. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Resource not found (or cross-workspace — never leaked). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '429': description: Rate limited. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' post: summary: Create a goal (per-plan cap; acyclic parent). tags: - Goals security: - bearerAuth: - goals:write parameters: - schema: type: string example: taskfolk description: Workspace slug. required: true description: Workspace slug. name: slug in: path requestBody: content: application/json: schema: $ref: '#/components/schemas/GoalCreateInput' responses: '201': description: Success. content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/Goal' required: - data '400': description: Validation error. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing / invalid API key. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: Key lacks the required scope. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Resource not found (or cross-workspace — never leaked). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '429': description: Rate limited. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' /v1/workspaces/{slug}/goals/{goalId}: get: summary: Get a goal with its rolled-up progress, status, key results, and alignment. tags: - Goals security: - bearerAuth: - goals:read parameters: - schema: type: string example: taskfolk description: Workspace slug. required: true description: Workspace slug. name: slug in: path - schema: type: string example: 019e6f12-… description: Goal id. required: true description: Goal id. name: goalId in: path responses: '200': description: Success. content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/GoalDetail' required: - data '400': description: Validation error. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing / invalid API key. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: Key lacks the required scope. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Resource not found (or cross-workspace — never leaked). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '429': description: Rate limited. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' patch: summary: Update a goal. tags: - Goals security: - bearerAuth: - goals:write parameters: - schema: type: string example: taskfolk description: Workspace slug. required: true description: Workspace slug. name: slug in: path - schema: type: string example: 019e6f12-… description: Goal id. required: true description: Goal id. name: goalId in: path requestBody: content: application/json: schema: $ref: '#/components/schemas/GoalPatchInput' responses: '200': description: Success. content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/Goal' required: - data '400': description: Validation error. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing / invalid API key. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: Key lacks the required scope. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Resource not found (or cross-workspace — never leaked). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '429': description: Rate limited. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' delete: summary: Delete a goal (soft). Child goals are orphaned (kept, unaligned). tags: - Goals security: - bearerAuth: - goals:write parameters: - schema: type: string example: taskfolk description: Workspace slug. required: true description: Workspace slug. name: slug in: path - schema: type: string example: 019e6f12-… description: Goal id. required: true description: Goal id. name: goalId in: path responses: '200': description: Success. content: application/json: schema: type: object properties: data: type: object properties: id: type: string deleted: type: boolean enum: - true required: - id - deleted required: - data '400': description: Validation error. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing / invalid API key. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: Key lacks the required scope. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Resource not found (or cross-workspace — never leaked). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '429': description: Rate limited. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' components: schemas: GoalCreateInput: type: object properties: title: type: string minLength: 1 maxLength: 200 description_md: type: - string - 'null' maxLength: 20000 portfolio_id: type: - string - 'null' description: Group under a portfolio, or omit/null for a company goal. parent_goal_id: type: - string - 'null' description: Alignment parent. Must stay acyclic and within the depth cap. owner_user_id: type: - string - 'null' cycle_start_at: type: - string - 'null' example: '2026-07-01' cycle_due_at: type: - string - 'null' example: '2026-09-30' required: - title Pagination: type: object properties: next_cursor: type: - string - 'null' example: null required: - next_cursor GoalPatchInput: type: object properties: title: type: string minLength: 1 maxLength: 200 description_md: type: - string - 'null' maxLength: 20000 portfolio_id: type: - string - 'null' parent_goal_id: type: - string - 'null' owner_user_id: type: - string - 'null' cycle_start_at: type: - string - 'null' cycle_due_at: type: - string - 'null' status_override: type: - string - 'null' enum: - on_track - at_risk - off_track - done - null description: Set a manual status, or null to clear the override. GoalDetail: allOf: - $ref: '#/components/schemas/Goal' - type: object properties: portfolio_name: type: - string - 'null' parent_title: type: - string - 'null' key_results: type: array items: type: object properties: id: type: string title: type: string kind: type: string enum: - manual - auto unit: type: - string - 'null' start_value: type: - number - 'null' target_value: type: - number - 'null' current_value: type: - number - 'null' weight: type: number scope: type: - object - 'null' properties: project_ids: type: array items: type: string issue_types: type: array items: type: string enum: - epic - story - task - bug - subtask label_ids: type: array items: type: string assignee_id: type: - string - 'null' description: Auto-KR work scope; null = inherit the goal's portfolio/workspace scope. Unknown ids are dropped. progress_pct: type: - integer - 'null' required: - id - title - kind - unit - start_value - target_value - current_value - weight - scope - progress_pct children: type: array items: type: object properties: id: type: string title: type: string progress_pct: type: - integer - 'null' status: type: string enum: - on_track - at_risk - off_track - done - not_started description: Derived roll-up status (override applied). not_started = no measured progress yet. required: - id - title - progress_pct - status watcher_count: type: integer required: - portfolio_name - parent_title - key_results - children - watcher_count ErrorEnvelope: type: object properties: error: type: object properties: code: type: string enum: - unauthorized - forbidden - not_found - validation - rate_limited - conflict - idempotency_violation - internal_error example: validation message: type: string example: Label "foo" does not exist on this project. details: type: object additionalProperties: {} required: - code - message required: - error Goal: type: object properties: id: type: string workspace_id: type: string title: type: string example: Reach 1,000 active workspaces description_md: type: - string - 'null' portfolio_id: type: - string - 'null' description: null = company goal (not grouped under a portfolio). parent_goal_id: type: - string - 'null' owner_user_id: type: - string - 'null' cycle_start_at: type: - string - 'null' format: date-time example: '2026-05-28T14:30:00Z' cycle_due_at: type: - string - 'null' format: date-time example: '2026-05-28T14:30:00Z' status_override: type: - string - 'null' enum: - on_track - at_risk - off_track - done - not_started - null description: Manual status override, or null when the status is fully derived. status: type: string enum: - on_track - at_risk - off_track - done - not_started description: Derived roll-up status (override applied). not_started = no measured progress yet. progress_pct: type: - integer - 'null' description: Rolled-up 0-100, or null when nothing is measured. created_by: type: string created_at: type: string format: date-time example: '2026-05-28T14:30:00Z' updated_at: type: string format: date-time example: '2026-05-28T14:30:00Z' required: - id - workspace_id - title - description_md - portfolio_id - parent_goal_id - owner_user_id - cycle_start_at - cycle_due_at - status_override - status - progress_pct - created_by - created_at - updated_at securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: API key description: 'Workspace API key. Send as `Authorization: Bearer tfk_live_…`. Each key is bound to exactly one workspace and carries a set of scopes.'