openapi: 3.2.0 info: title: Goalkeeper Goals API version: 0.0.0 description: Public REST API. servers: - url: http://localhost:3001 description: Local development tags: - name: Goals description: Organization goals and their label taxonomy. paths: /v1/goals: get: operationId: listGoals summary: List goals description: Lists goals in the credential's organization, constrained by its own-goals or all-goals scope. tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: status in: query required: false schema: $ref: '#/components/schemas/GoalStatus' - name: health in: query required: false schema: $ref: '#/components/schemas/GoalHealth' - name: ownerUserId in: query required: false schema: type: string minLength: 1 maxLength: 200 - name: labelId in: query required: false schema: type: string format: uuid responses: '200': description: Goals visible to the caller. content: application/json: schema: $ref: '#/components/schemas/ListGoalsResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' post: operationId: createGoal summary: Publish a goal description: Publishes a durable goal in active status. The owner defaults to the caller and the title defaults to a concise form of the detailed description. tags: - Goals security: - bearerAuth: [] - cookieAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateGoalRequest' responses: '201': description: The created goal. content: application/json: schema: $ref: '#/components/schemas/GoalResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/goals/{goalId}: get: operationId: getGoal summary: Get a goal tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: goalId in: path required: true schema: type: string format: uuid responses: '200': description: The goal. content: application/json: schema: $ref: '#/components/schemas/GoalResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' patch: operationId: updateGoal summary: Update goal metadata description: Updates descriptive metadata. Report a goal update to change lifecycle status. tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: goalId in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateGoalRequest' responses: '200': description: The updated goal. content: application/json: schema: $ref: '#/components/schemas/GoalResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' delete: operationId: deleteGoal summary: Delete a goal description: Permanently deletes a goal together with its label assignments and status history. tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: goalId in: path required: true schema: type: string format: uuid responses: '204': description: The goal was deleted. '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/goals/{goalId}/updates: get: operationId: listGoalUpdates summary: List goal updates description: Returns the append-only status history for a goal in revision order. tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: goalId in: path required: true schema: type: string format: uuid responses: '200': description: The goal's status history. content: application/json: schema: $ref: '#/components/schemas/ListGoalUpdatesResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' post: operationId: createGoalUpdate summary: Report a goal update description: Appends a status report and advances the goal's current status and revision atomically. tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: goalId in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateGoalUpdateRequest' responses: '201': description: The appended goal update. content: application/json: schema: $ref: '#/components/schemas/GoalUpdateResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/goal-labels: get: operationId: listGoalLabels summary: List goal labels tags: - Goals security: - bearerAuth: [] - cookieAuth: [] responses: '200': description: Goal labels in the credential's organization. content: application/json: schema: $ref: '#/components/schemas/ListGoalLabelsResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' post: operationId: createGoalLabel summary: Create a goal label tags: - Goals security: - bearerAuth: [] - cookieAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateGoalLabelRequest' responses: '201': description: The created goal label. content: application/json: schema: $ref: '#/components/schemas/GoalLabelResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/goal-labels/{labelId}: get: operationId: getGoalLabel summary: Get a goal label tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: labelId in: path required: true schema: type: string format: uuid responses: '200': description: The goal label. content: application/json: schema: $ref: '#/components/schemas/GoalLabelResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' patch: operationId: updateGoalLabel summary: Update a goal label tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: labelId in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateGoalLabelRequest' responses: '200': description: The updated goal label. content: application/json: schema: $ref: '#/components/schemas/GoalLabelResponse' '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' delete: operationId: deleteGoalLabel summary: Delete an unused goal label tags: - Goals security: - bearerAuth: [] - cookieAuth: [] parameters: - name: labelId in: path required: true schema: type: string format: uuid responses: '204': description: The goal label was deleted. '400': description: The request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The credential lacks the required scope or authority. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The resource was not found or is not visible to the caller. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The requested state conflicts with an existing resource. content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: GoalResponse: type: object additionalProperties: false required: - goal properties: goal: $ref: '#/components/schemas/Goal' GoalLabel: type: object additionalProperties: false required: - id - organizationId - name - color - description - createdAt - createdByUserId - updatedAt - updatedByUserId properties: id: type: string format: uuid organizationId: type: string format: uuid name: type: string minLength: 1 maxLength: 64 color: type: - string - 'null' minLength: 1 maxLength: 32 description: type: - string - 'null' minLength: 1 maxLength: 500 createdAt: type: string format: date-time createdByUserId: type: string minLength: 1 updatedAt: type: string format: date-time updatedByUserId: type: string minLength: 1 GoalHealth: type: string enum: - on_track - at_risk - off_track UpdateGoalRequest: type: object additionalProperties: false minProperties: 1 properties: title: type: string minLength: 1 maxLength: 200 detailedDescription: type: string minLength: 1 description: A long-form description that may contain Markdown. timeframe: $ref: '#/components/schemas/GoalTimeframe' ownerUserId: type: - string - 'null' minLength: 1 maxLength: 200 labelIds: type: array maxItems: 20 uniqueItems: true items: type: string format: uuid criteria: type: array maxItems: 100 items: $ref: '#/components/schemas/GoalCriterion' GoalEvaluationResult: type: string enum: - met - not_met - unknown CreateGoalRequest: type: object additionalProperties: false required: - detailedDescription - timeframe properties: title: type: string minLength: 1 maxLength: 200 detailedDescription: type: string minLength: 1 description: A long-form description that may contain Markdown. timeframe: $ref: '#/components/schemas/PublishedGoalTimeframe' ownerUserId: type: - string - 'null' minLength: 1 maxLength: 200 labelIds: type: array maxItems: 20 uniqueItems: true items: type: string format: uuid criteria: type: array maxItems: 100 items: $ref: '#/components/schemas/GoalCriterion' GoalEvaluation: type: object additionalProperties: false required: - result - asOf properties: result: $ref: '#/components/schemas/GoalEvaluationResult' asOf: type: string format: date-time UpdateGoalLabelRequest: type: object additionalProperties: false minProperties: 1 properties: name: type: string minLength: 1 maxLength: 64 color: type: - string - 'null' minLength: 1 maxLength: 32 description: type: - string - 'null' minLength: 1 maxLength: 500 GoalActor: oneOf: - type: object additionalProperties: false required: - kind - id - runId properties: kind: const: user id: type: string minLength: 1 maxLength: 200 runId: type: 'null' - type: object additionalProperties: false required: - kind - id - runId properties: kind: const: client id: type: string minLength: 1 maxLength: 200 runId: type: 'null' - type: object additionalProperties: false required: - kind - id - runId properties: kind: const: agent id: type: string minLength: 1 maxLength: 200 runId: type: - string - 'null' minLength: 1 maxLength: 200 CreateGoalLabelRequest: type: object additionalProperties: false required: - name properties: name: type: string minLength: 1 maxLength: 64 color: type: - string - 'null' minLength: 1 maxLength: 32 description: type: - string - 'null' minLength: 1 maxLength: 500 GoalCriterion: type: object additionalProperties: false required: - title - description properties: title: type: string minLength: 1 maxLength: 200 description: type: string minLength: 1 maxLength: 10000 GoalClientInfo: type: object additionalProperties: false required: - name - version properties: name: type: string minLength: 1 maxLength: 200 version: type: string minLength: 1 maxLength: 100 GoalUpdate: type: object additionalProperties: false required: - id - organizationId - goalId - revision - status - health - evaluation - summary - details - authorityUserId - actor - authentication - clientInfo - idempotencyKey - createdAt properties: id: type: string format: uuid organizationId: type: string format: uuid goalId: type: string format: uuid revision: type: integer minimum: 1 status: $ref: '#/components/schemas/GoalStatus' health: anyOf: - $ref: '#/components/schemas/GoalHealth' - type: 'null' evaluation: anyOf: - $ref: '#/components/schemas/GoalEvaluation' - type: 'null' summary: type: string minLength: 1 maxLength: 500 details: type: string minLength: 1 description: A long-form status report that may contain Markdown. authorityUserId: type: string minLength: 1 actor: $ref: '#/components/schemas/GoalActor' authentication: $ref: '#/components/schemas/GoalAuthentication' clientInfo: anyOf: - $ref: '#/components/schemas/GoalClientInfo' - type: 'null' idempotencyKey: type: string minLength: 1 maxLength: 200 createdAt: type: string format: date-time GoalAuthentication: type: object additionalProperties: false required: - kind - subjectId properties: kind: type: string enum: - session - api_token - oauth - unknown subjectId: type: - string - 'null' minLength: 1 Goal: type: object additionalProperties: false required: - id - organizationId - title - detailedDescription - status - health - timeframe - currentEvaluation - ownerUserId - labels - criteria - revision - createdAt - createdByUserId - updatedAt - updatedByUserId properties: id: type: string format: uuid organizationId: type: string format: uuid title: type: string minLength: 1 maxLength: 200 detailedDescription: type: string minLength: 1 description: A long-form description that may contain Markdown. status: $ref: '#/components/schemas/GoalStatus' health: anyOf: - $ref: '#/components/schemas/GoalHealth' - type: 'null' timeframe: $ref: '#/components/schemas/GoalTimeframe' currentEvaluation: anyOf: - $ref: '#/components/schemas/GoalEvaluation' - type: 'null' ownerUserId: type: - string - 'null' minLength: 1 maxLength: 200 labels: type: array maxItems: 20 items: $ref: '#/components/schemas/GoalLabel' criteria: type: array maxItems: 100 items: $ref: '#/components/schemas/GoalCriterion' revision: type: integer minimum: 1 createdAt: type: string format: date-time createdByUserId: type: string minLength: 1 updatedAt: type: string format: date-time updatedByUserId: type: string minLength: 1 Error: type: object additionalProperties: false required: - error properties: error: type: string message: type: string ListGoalUpdatesResponse: type: object additionalProperties: false required: - updates properties: updates: type: array items: $ref: '#/components/schemas/GoalUpdate' GoalStatus: type: string enum: - active - completed - paused - archived GoalLabelResponse: type: object additionalProperties: false required: - label properties: label: $ref: '#/components/schemas/GoalLabel' CreateGoalUpdateRequest: type: object additionalProperties: false required: - status - summary - details - expectedRevision - idempotencyKey properties: status: $ref: '#/components/schemas/GoalStatus' health: anyOf: - $ref: '#/components/schemas/GoalHealth' - type: 'null' description: A newly reported health value. Omit or send null to retain the current health; completed and archived statuses clear it. evaluation: anyOf: - $ref: '#/components/schemas/GoalEvaluation' - type: 'null' summary: type: string minLength: 1 maxLength: 500 details: type: string minLength: 1 expectedRevision: type: integer minimum: 1 idempotencyKey: type: string minLength: 1 maxLength: 200 ListGoalsResponse: type: object additionalProperties: false required: - goals properties: goals: type: array items: $ref: '#/components/schemas/Goal' GoalUpdateResponse: type: object additionalProperties: false required: - update properties: update: $ref: '#/components/schemas/GoalUpdate' ListGoalLabelsResponse: type: object additionalProperties: false required: - labels properties: labels: type: array items: $ref: '#/components/schemas/GoalLabel' PublishedGoalTimeframe: oneOf: - type: object additionalProperties: false required: - kind properties: kind: const: continuous - type: object additionalProperties: false required: - kind - targetDate properties: kind: const: deadline targetDate: type: string format: date GoalTimeframe: oneOf: - type: object additionalProperties: false required: - kind properties: kind: const: unspecified - type: object additionalProperties: false required: - kind properties: kind: const: continuous - type: object additionalProperties: false required: - kind - targetDate properties: kind: const: deadline targetDate: type: string format: date securitySchemes: bearerAuth: type: http scheme: bearer description: A Goalkeeper API token or provider-issued OAuth access token. cookieAuth: type: apiKey in: cookie name: goalkeeper_session