Performance

Manage OKRs, appraisals, and promotions via the API.

The Performance endpoints cover three areas — objectives (OKRs), appraisals, and promotions — for your own employees.

Call https://api.kuahr.com with the company’s authorised Live key in KUA_API_KEY. The SDKs use this address by default: Node or Python. Replace the illustrative IDs with real records from this company; the public API does not list employees. Persist an operation-specific idempotency key before writes and reuse the exact request on interrupted responses.

Objectives (OKRs)

An objective has key results and progress check-ins.

# Create an objective with a key result
curl "https://api.kuahr.com/v1/performance/objectives" \
  -X POST -H "X-Api-Key: kua_live_your_key_here" -H "Content-Type: application/json" \
  -d '{
    "title": "Grow monthly active users",
    "cycleLabel": "2026 Q3",
    "cycleStart": "2026-07-01",
    "cycleEnd": "2026-09-30",
    "keyResults": [{ "description": "Reach 50k MAU", "targetValue": 50000, "unit": "users" }]
  }'

# List / get
curl "https://api.kuahr.com/v1/performance/objectives?cycleLabel=2026%20Q3" -H "X-Api-Key: kua_live_your_key_here"
const objective = await kua.performance.objectives.create({
  title: "Grow monthly active users",
  cycleLabel: "2026 Q3",
  cycleStart: "2026-07-01",
  cycleEnd: "2026-09-30",
  keyResults: [{ description: "Reach 50k MAU", targetValue: 50000, unit: "users" }],
});

await kua.performance.objectives.list({ cycleLabel: "2026 Q3" });
await kua.performance.keyResults.update(objective.keyResults[0].id, { currentValue: 12000, confidenceScore: 0.7 });
await kua.performance.checkIns.create(objective.objective.id, { note: "On track", confidence: 0.7 });
objective = kua.performance.objectives.create(
    title="Grow monthly active users", cycle_label="2026 Q3",
    cycle_start="2026-07-01", cycle_end="2026-09-30",
    key_results=[{"description": "Reach 50k MAU", "targetValue": 50000, "unit": "users"}],
)

kua.performance.objectives.list(cycle_label="2026 Q3")
kua.performance.key_results.update(objective.key_results[0].id, current_value=12000, confidence_score=0.7)
kua.performance.check_ins.create(objective.objective.id, note="On track", confidence=0.7)

Appraisals

Creating a cycle seeds a review for every active employee. Self and manager submissions are separate steps: self submission can move PendingSelf to PendingManager, while manager submission records ReviewsSubmitted and may precede a self submission. Submitted reviews are not a completed/calibrated appraisal; completion is a separate authorised product flow.

curl "https://api.kuahr.com/v1/performance/appraisal-cycles" \
  -X POST -H "X-Api-Key: kua_live_your_key_here" -H "Content-Type: application/json" \
  -d '{ "name": "H2 2026 review", "opensOn": "2026-10-01", "closesOn": "2026-10-31" }'

curl "https://api.kuahr.com/v1/performance/appraisal-cycles/{id}/reviews" -H "X-Api-Key: kua_live_your_key_here"

curl -X POST "https://api.kuahr.com/v1/performance/reviews/{id}/manager" \
  -H "X-Api-Key: kua_live_your_key_here" -H "Content-Type: application/json" \
  -d '{ "body": "Great half", "rating": 4 }'
const cycle = await kua.performance.appraisalCycles.create({ name: "H2 2026 review", opensOn: "2026-10-01", closesOn: "2026-10-31" });
const reviews = await kua.performance.appraisalCycles.reviews(cycle.id);
const review = reviews.find(item => item.employeeId === intendedEmployeeId);
if (!review) throw new Error("No eligible review for the intended employee");
// Only when each role is authorised and the current review state permits it:
await kua.performance.reviews.submitSelf(review.id, { body: "My self assessment", rating: 4 });
await kua.performance.reviews.submitManager(review.id, { body: "Manager assessment", rating: 4 });
cycle = kua.performance.appraisal_cycles.create(name="H2 2026 review", opens_on="2026-10-01", closes_on="2026-10-31")
reviews = kua.performance.appraisal_cycles.reviews(cycle.id)
review = next((row for row in reviews if row.employee_id == intended_employee_id), None)
if review is None:
    raise ValueError("No eligible review for the intended employee")
# Each submission needs its intended actor's authority and eligible current state.
kua.performance.reviews.submit_self(review.id, body="My self assessment", rating=4)
kua.performance.reviews.submit_manager(review.id, body="Manager assessment", rating=4)

Promotions

An append-only record of role/salary changes (amounts in minor units, e.g. kobo):

curl "https://api.kuahr.com/v1/performance/promotions" \
  -X POST -H "X-Api-Key: kua_live_your_key_here" -H "Content-Type: application/json" \
  -d '{ "employeeId": "b2c3…", "fromTitle": "Engineer", "toTitle": "Senior Engineer", "toSalaryMinor": 55000000, "effectiveDate": "2026-08-01" }'

curl "https://api.kuahr.com/v1/performance/employees/{employeeId}/promotions" -H "X-Api-Key: kua_live_your_key_here"
await kua.performance.promotions.create({ employeeId: "b2c3…", fromTitle: "Engineer", toTitle: "Senior Engineer", toSalaryMinor: 55_000_000, effectiveDate: "2026-08-01" });
await kua.performance.promotions.list("b2c3…");
kua.performance.promotions.create(employee_id="b2c3…", from_title="Engineer", to_title="Senior Engineer", to_salary_minor=55_000_000, effective_date="2026-08-01")
kua.performance.promotions.list("b2c3…")

Verify each workflow

WorkflowOrdered actionsCompletion and alternatives
ObjectivesCreate → retain objective/key-result IDs → update a key result → add a check-in → GET objective detailRead the detail again and verify progress. An empty list needs a deliberate create, not indexing its first item.
AppraisalCreate/list cycle → list reviews → select intended employee → authorised self submission → authorised manager submissionSelf submission uses POST /v1/performance/reviews/{id}/self; re-read the cycle’s reviews after each applicable step. No eligible employees can mean no reviews. Do not submit another employee’s review to finish an example.
PromotionRecord approved role/compensation change → list that employee’s promotionsVerify the returned record and effective date. Recording a promotion does not update payroll salary or disburse pay; coordinate the separately authorised salary-structure handoff.

Self and manager submissions are different responsibilities, not permission to impersonate another actor. The snippets illustrate the lifecycle only; use the credential/actor authorised for each step and inspect state before submitting. A failed/duplicate/out-of-order submission requires current-state readback, not a loop through transitions.

The objective detail route is GET /v1/performance/objectives/{id}; key-result progress is PATCH /v1/performance/key-results/{id}. The API reference provides exact request schemas. Use the matching read/write grants, retain write keys and handle missing IDs or empty lists without producing unintended records.

Done when: the intended objective/review/promotion is present with the expected state, and any separate payroll handoff has its own owner and evidence. Continue with go-live readiness.

Search documentation

↑ ↓ to move · Enter to open · Esc to close