# OKR Dashboard with Kotlin Client

In this tutorial, we’ll build a small **Kotlin** command-line program that interacts with the **Peoplelogic.dev API** to manage OKRs (Objectives and Key Results) using pure HTTP calls. We’ll cover the following steps:

1. **Authentication:** Using a JWT API key as a Bearer token for authorization.
2. **Create an Objective:** Making a REST call to create a top-level OKR (Objective).
3. **Add Key Results:** Creating child objectives (key results) under the main objective.
4. **Add Tasks:** Attaching action-item tasks to the OKRs.
5. **Fetch & Display:** Retrieving all OKRs and printing them in a hierarchical, dashboard-like view in the console.

We’ll use **Gradle** to set up a Kotlin project (JVM) with **OkHttp** for HTTP requests and a simple JSON library for parsing. The style here is hands-on and explanatory (in the spirit of CrewAI and Spring AI tutorials), with plenty of comments and clarity for a backend engineer with moderate Kotlin experience.

> **Note:** Replace placeholders (like `<YOUR_ORG_ID>` and `<YOUR_API_KEY>`) with your actual Peoplelogic organization ID and API key. You can obtain these from your Peoplelogic account. The API key is a JWT token that includes your org credentials and must be included as a Bearer token in each request.

### Project Setup

Let's start by setting up a new Kotlin project with Gradle. We will need the Kotlin JVM plugin, and dependencies for OkHttp (for HTTP calls) and org.json (for JSON handling).

**Gradle Build Script (build.gradle.kts):** This configures the project and dependencies.

```kotlin
plugins {
    kotlin("jvm") version "1.8.20"
    application
}
group = "com.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.10.0")
    implementation("org.json:json:20211205")
}

application {
    mainClass.set("com.example.MainKt")
}
```

A few notes on the setup:

* We apply the Kotlin JVM plugin and set a Kotlin version (1.8.20 here).
* Dependencies include **OkHttp 4.x** for HTTP requests and **org.json** for simple JSON parsing. Both will be pulled from Maven Central.
* We use the Gradle Application plugin to easily run the program (with `MainKt` as the entry point).

Our project structure will look like this:

```
PeoplelogicKotlinDemo/
├── build.gradle.kts
├── settings.gradle.kts
└── src/main/kotlin/com/example/Main.kt
```

> The complete source code is provided in the attached ZIP file at the end of this tutorial.

### Authenticating with a JWT Bearer Token

The Peoplelogic API expects each request to be authenticated with a JWT token in the `Authorization` header. In our code, we’ll store the Org ID and API key (JWT) as constants and include them in every request.

The base URL for the Peoplelogic API is `https://api.peoplelogic.dev/api/v1`. All our endpoint paths will be relative to this (e.g. `/objective` for OKRs). The JWT token provided by Peoplelogic should be included as a **Bearer** token in the HTTP Authorization header.

We’ll set up some constants and an OkHttp client in our `Main.kt`:

```kotlin
package com.example

import okhttp3.*
import org.json.JSONObject

fun main() {
    // Placeholder credentials (replace with your actual values)
    val ORG_ID = "<YOUR_ORG_ID>"
    val API_KEY = "<YOUR_API_KEY>"  // JWT token for your Peoplelogic API
    val BASE_URL = "https://api.peoplelogic.dev/api/v1"

    // Initialize HTTP client
    val client = OkHttpClient()
    // Media type for JSON payloads
    val JSONMedia = MediaType.get("application/json; charset=utf-8")
    ...
}
```

Here we set `API_KEY` to your JWT token string. We’ll use this in the header of every request like so:

```kotlin
.header("Authorization", "Bearer $API_KEY")
```

This ensures the Peoplelogic API recognizes our calls as authorized.

### 1. Creating a Top-Level Objective

An **Objective** in Peoplelogic represents a high-level goal (for example, *“Increase Q3 Sales by 20%”*). We’ll create a new objective via an HTTP **POST** request to the `/objective` endpoint.

**Endpoint:** `POST /objective` (requires auth) – Creates a new objective in your organization. The request body is JSON with the objective details.

```kotlin
// 1. Create a top-level Objective (OKR)
val objectiveJson = JSONObject(mapOf(
    "name" to "Increase Q3 Sales by 20%",
    "description" to "Company-wide sales growth goal for Q3"
))
val createObjectiveRequest = Request.Builder()
    .url("$BASE_URL/objective")
    .header("Authorization", "Bearer $API_KEY")
    .post(RequestBody.create(JSONMedia, objectiveJson.toString()))
    .build()

val createObjectiveResponse = client.newCall(createObjectiveRequest).execute()
if (!createObjectiveResponse.isSuccessful) {
    println("Failed to create objective: HTTP ${createObjectiveResponse.code}")
    return
}
val createdObjective = JSONObject(createObjectiveResponse.body!!.string())
val objectiveId = createdObjective.getString("id")
println("Created Objective '${createdObjective.getString("name")}' (ID: $objectiveId)")
```

### 2. Adding Key Results (Child Objectives)

With the main objective in place, we’ll add a couple of **Key Results**. In Peoplelogic, key results are objectives that have a `parentId` linking them to a top-level objective.

```kotlin
// 2. Create Key Results (child objectives)
val keyResults = listOf("Achieve $1M in new revenue", "Improve conversion rate to 5%")
val keyResultIds = mutableListOf<String>()

for (krName in keyResults) {
    val krJson = JSONObject(mapOf(
        "name" to krName,
        "parentId" to objectiveId          
    ))
    val createKrRequest = Request.Builder()
        .url("$BASE_URL/objective")
        .header("Authorization", "Bearer $API_KEY")
        .post(RequestBody.create(JSONMedia, krJson.toString()))
        .build()
    val createKrResponse = client.newCall(createKrRequest).execute()
    if (!createKrResponse.isSuccessful) {
        println("Failed to create key result: HTTP ${createKrResponse.code}")
        return
    }
    val createdKr = JSONObject(createKrResponse.body!!.string())
    val krId = createdKr.getString("id")
    keyResultIds.add(krId)
    println("Created Key Result '${createdKr.getString("name")}' (ID: $krId)")
}
```

### 3. Adding Tasks to the OKRs

Next, we’ll add tasks to each key result via the `POST /entity/{entityId}/task` endpoint.

```kotlin
// 3. Add Tasks (action items)
val tasks = listOf(
    "Identify 50 new sales leads" to "2025-09-30",
    "Launch marketing campaign"   to "2025-09-30"
)
for ((index, krId) in keyResultIds.withIndex()) {
    val (taskName, dueDate) = tasks[index]
    val taskJson = JSONObject(mapOf(
        "name" to taskName,
        "dueDate" to dueDate
    ))
    val createTaskRequest = Request.Builder()
        .url("$BASE_URL/entity/$krId/task")
        .header("Authorization", "Bearer $API_KEY")
        .post(RequestBody.create(JSONMedia, taskJson.toString()))
        .build()
    val createTaskResponse = client.newCall(createTaskRequest).execute()
    if (!createTaskResponse.isSuccessful) {
        println("Failed to create task: HTTP ${createTaskResponse.code}")
        return
    }
    val updatedObjective = JSONObject(createTaskResponse.body!!.string())
    val task = updatedObjective.getJSONArray("tasks").getJSONObject(0)
    println("Added Task '${task.getString("name")}' to Key Result (ID: $krId)")
}
```

### 4. Fetching and Displaying the OKR Dashboard

Finally, retrieve all objectives with child objectives and tasks via:

```
GET /objective?projections=OBJECTIVE_TREE,TASKS
```

Then print a neat CLI dashboard:

```kotlin
val getOkrsRequest = Request.Builder()
    .url("$BASE_URL/objective?projections=OBJECTIVE_TREE,TASKS")
    .header("Authorization", "Bearer $API_KEY")
    .get()
    .build()

val getOkrsResponse = client.newCall(getOkrsRequest).execute()
...
val okrsArray = JSONArray(getOkrsResponse.body!!.string())
println("\nOKR Dashboard:")
for (i in 0 until okrsArray.length()) {
    val obj = okrsArray.getJSONObject(i)
    printObjective(obj, 0)
}

// Helper to print objectives and tasks recursively
fun printObjective(obj: JSONObject, indent: Int) {
    val indentStr = " ".repeat(indent * 2)
    val label = if (obj.isNull("parentId")) "Objective" else "Key Result"
    println("$indentStr$label: ${obj.getString("name")}")
    if (obj.has("tasks")) {
        val tasks = obj.getJSONArray("tasks")
        for (j in 0 until tasks.length()) {
            val task = tasks.getJSONObject(j)
            val status = task.getJSONObject("status").getString("current")
            val checkbox = if (status == "ACTIVE") "[ ]" else "[x]"
            val due = if (task.isNull("dueDate")) "" else " (due ${task.getString("dueDate")})"
            println("$indentStr  $checkbox ${task.getString("name")}$due")
        }
    }
    if (obj.has("childObjectives")) {
        val children = obj.getJSONArray("childObjectives")
        for (k in 0 until children.length()) {
            printObjective(children.getJSONObject(k), indent + 1)
        }
    }
}
```

**Expected Console Output:**

```
OKR Dashboard:
Objective: Increase Q3 Sales by 20%
  Key Result: Achieve $1M in new revenue
    [ ] Identify 50 new sales leads (due 2025-09-30)
  Key Result: Improve conversion rate to 5%
    [ ] Launch marketing campaign (due 2025-09-30)
```

That’s it! You’ve built a full CLI dashboard for OKRs using **only** the Peoplelogic.dev REST API and **basic** Kotlin libraries.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.peoplelogic.dev/tutorials/okr-dashboard-with-kotlin-client.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
