Performance API
Cycle Endpoints
Section titled “Cycle Endpoints”GET /api/performance/cycles
Section titled “GET /api/performance/cycles”List review cycles. Non-executive users only see open/closed cycles.
Permission: view:performance
POST /api/performance/cycles
Section titled “POST /api/performance/cycles”Create a new review cycle.
Permission: manage:performance
Body:
{ "type": "quarterly", "name": "Q1 2026 Review", "period_start": "2026-01-01", "period_end": "2026-03-31", "due_date": "2026-04-15"}GET /api/performance/cycles/:id
Section titled “GET /api/performance/cycles/:id”Get cycle detail with review progress stats (total, completed, draft, self_assessment, in_review, ready_for_review).
Permission: view:performance
PUT /api/performance/cycles/:id
Section titled “PUT /api/performance/cycles/:id”Update cycle fields (name, dates, status).
Permission: manage:performance
Body:
{ "status": "open"}DELETE /api/performance/cycles/:id
Section titled “DELETE /api/performance/cycles/:id”Delete a draft cycle. Returns 400 if cycle is not in draft status.
Permission: manage:performance
My Goals
Section titled “My Goals”GET /api/performance/my-goals
Section titled “GET /api/performance/my-goals”Returns goals from the current user’s most recent completed review. Used by the dashboard to display current goals.
Permission: view:performance
Response:
{ "goals": [ { "id": "goal-1", "review_id": "review-abc", "goal": "Complete AI certification", "success_measures": "Certificate obtained", "support_needed": "Course budget", "status": "completed", "sort_order": 0 } ], "cycle_name": "Q4 2025 Review"}Returns { "goals": [], "cycle_name": "" } if the user has no completed reviews.
Review Endpoints
Section titled “Review Endpoints”GET /api/performance/reviews
Section titled “GET /api/performance/reviews”List reviews, scoped by access level. Supports filtering.
Permission: view:performance
Query params:
cycle_id— filter by cyclestatus— filter by review status
POST /api/performance/reviews
Section titled “POST /api/performance/reviews”Create a review for a user in an open cycle. Snapshots the user’s review template KPI items into review_kpi_ratings.
Permission: update:performance
Body:
{ "cycle_id": "cycle-abc", "user_id": "user-xyz", "reviewer_id": "user-reviewer"}reviewer_iddefaults to the requesting user if omitted- Returns 409 if a review already exists for the user in this cycle
GET /api/performance/reviews/:id
Section titled “GET /api/performance/reviews/:id”Full review detail with KPI ratings and goals.
Permission: view:performance
Response:
{ "review": { "id": "review-abc", "status": "in_review", "user_name": "Jane Smith", "reviewer_name": "John Manager", "cycle_name": "Q1 2026 Review", "cycle_type": "quarterly", "kpi_pass_count": null, "kpi_total_count": null, "strengths": "...", "growth_areas": "...", "discussion_topics": "..." }, "kpi_ratings": [ { "id": "kpi-1", "area": "performance", "label": "Billable hours target", "rating": "achieving", "notes": null, "sort_order": 0 } ], "goals": [ { "id": "goal-1", "goal": "Complete AI certification", "success_measures": "Certificate obtained", "support_needed": "Course budget", "status": null, "sort_order": 0 } ], "previous_reviews": [ { "review": { "id": "prev-review-1", "cycle_name": "Q4 2025 Review", "reviewer_notes": "Strong quarter.", "action_items": "Continue AI focus." }, "kpi_ratings": [{ "template_item_id": "tpl-1", "rating": "achieving" }], "goals": [{ "id": "goal-old", "goal": "Learn React", "status": "completed" }] } ], "kpi_aggregation": null}For annual reviews, previous_reviews contains all completed quarterly reviews within the annual period, and kpi_aggregation provides a per-KPI summary:
{ "kpi_aggregation": { "tpl-item-1": { "ratings": { "achieving": 3, "exceeding": 1 }, "most_common": "achieving" } }}PUT /api/performance/reviews/:id/previous-goals
Section titled “PUT /api/performance/reviews/:id/previous-goals”Update goal outcomes from previous reviews (mark as completed/not completed).
Permission: update:performance
Body:
{ "outcomes": [ { "goal_id": "goal-old", "status": "completed" }, { "goal_id": "goal-old-2", "status": "not_completed" } ]}GET /api/performance/reviews/history/:userId
Section titled “GET /api/performance/reviews/history/:userId”Lightweight trend data: completed reviews with KPI pass counts and goal completion counts.
Permission: view:performance
Response:
{ "history": [ { "review_id": "review-1", "cycle_id": "cycle-abc", "cycle_name": "Q1 2026", "cycle_type": "quarterly", "period_start": "2026-01-01", "period_end": "2026-03-31", "completed_at": "2026-04-01", "kpi_pass_count": 8, "kpi_total_count": 10, "kpi_passed": 1, "goal_count": 3, "goals_completed": 2, "goals_not_completed": 1 } ]}PUT /api/performance/reviews/:id
Section titled “PUT /api/performance/reviews/:id”Update review fields, KPI ratings, and goals. On status transition to completed, automatically computes KPI pass/fail.
Permission: update:performance
Body:
{ "status": "completed", "strengths": "Strong delivery...", "growth_areas": "Could improve...", "discussion_topics": "Career progression, training budget", "reviewer_notes": "Solid quarter.", "kpi_ratings": [ { "id": "kpi-1", "rating": "achieving", "notes": "Met target" } ], "goals": [ { "goal": "AI cert", "success_measures": "Done", "sort_order": 0 } ]}Pass/fail logic on completion:
- Counts KPIs with “achieving” or “exceeding” as passed
- Checks mandatory items (via
review_template_items.is_mandatory) - Requires ≥75% pass rate and all mandatory items passed
DELETE /api/performance/reviews/:id
Section titled “DELETE /api/performance/reviews/:id”Delete a draft review only.
Permission: manage:performance
Feedback Endpoints
Section titled “Feedback Endpoints”GET /api/performance/feedback/requests
Section titled “GET /api/performance/feedback/requests”List pending feedback requests assigned to the current user.
Permission: view:performance
POST /api/performance/feedback/requests
Section titled “POST /api/performance/feedback/requests”Create an individual feedback request.
Permission: update:performance
Body:
{ "cycle_id": "cycle-abc", "subject_id": "user-xyz", "reviewer_id": "user-peer", "due_date": "2026-04-01"}POST /api/performance/feedback/requests/bulk
Section titled “POST /api/performance/feedback/requests/bulk”Auto-select peers for a subject based on their role and create feedback requests.
Permission: update:performance
Body:
{ "cycle_id": "cycle-abc", "subject_id": "user-xyz", "due_date": "2026-04-01"}Peer selection logic:
- Employee/Lead: Squad peers
- Manager: Other managers + direct reports
- Head/Executive: Other heads + direct manager reports
Response: { "created": 5, "total_peers": 5 }
POST /api/performance/feedback/responses
Section titled “POST /api/performance/feedback/responses”Submit feedback for a pending request. Marks the request as submitted.
Permission: view:performance
Body:
{ "request_id": "req-abc", "organisation_rating": "achieving", "organisation_comment": "Good time management", "performance_rating": "exceeding", "performance_comment": "Consistently exceeds targets", "communication_rating": "achieving", "development_rating": "achieving", "automation_rating": "achieving", "automation_comment": "Great AI adoption", "overall_comment": "Strong contributor"}GET /api/performance/feedback/summary/:userId
Section titled “GET /api/performance/feedback/summary/:userId”Anonymized aggregate feedback for a user. Manager+ access only.
Permission: view:performance (manager+ access level required)
Query params:
cycle_id— optional filter by cycle
Returns { "sufficient": false, "response_count": 0, "minimum_required": 1 } if no responses.
With sufficient responses:
{ "sufficient": true, "response_count": 5, "areas": { "organisation": { "ratings": { "achieving": 3, "exceeding": 2 }, "comments": ["Good time management", "..."] } }, "overall_comments": ["Strong contributor", "..."]}Comments are shuffled for anonymity.
Alerts
Section titled “Alerts”GET /api/performance/alerts
Section titled “GET /api/performance/alerts”Pending action items for the current user.
Permission: view:performance
Response:
{ "pending_reviews": 2, "self_assessments": 1, "pending_feedback": 3, "open_cycles": 1}