LogoLogo
WebsiteChangelogBook a Demo
  • Welcome to Peoplelogic.dev
  • API Documentation
    • Page 1
  • Guides
    • Page 2
  • Tutorials
    • OKR Dashboard with Kotlin Client
    • Managing Meetings with Kotlin and OkHttp
    • Using Webhooks for Slack Notifications
Powered by GitBook

2025 Peoplelogic, Inc.

On this page
  • Gradle Project Setup
  • 1. Create a Webhook Signing Key
  • 2. Register a Webhook for Objective Status Changes
  • 3. Create and Complete an OKR Objective
  • Example API Output Summary
  • 4. Ktor Webhook Listener
  1. Tutorials

Using Webhooks for Slack Notifications

Webhooks are a way for services to send real-time HTTP callbacks to your application when certain events occur. Instead of polling an API repeatedly, your app can subscribe to updates and receive an HTTP POST as soon as something changes. This is perfect for building event-driven integrations: for example, Peoplelogic can notify your app immediately when an OKR (or any other entity like an IDP or Meeting) is marked COMPLETED, and you can handle it right away—say, by sending a Slack message. Let's dive in!

Gradle Project Setup

Let’s set up a Kotlin/Gradle project with the dependencies you need. In your build.gradle.kts, include Ktor for both server and client, plus kotlinx.serialization:

plugins {
    kotlin("jvm") version "1.8.21"
    application
}

repositories {
    mavenCentral()
}

dependencies {
    // Ktor server
    implementation("io.ktor:ktor-server-core:2.3.1")
    implementation("io.ktor:ktor-server-netty:2.3.1")
    implementation("io.ktor:ktor-server-content-negotiation:2.3.1")
    implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.1")

    // Ktor client (for Slack integration)
    implementation("io.ktor:ktor-client-cio:2.3.1")
    implementation("io.ktor:ktor-client-content-negotiation:2.3.1")
    implementation("io.ktor:ktor-client-logging:2.3.1")

    // Kotlinx JSON
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}

application {
    mainClass.set("MainKt")
}

This setup gives you everything you need to both serve webhook callbacks and call external HTTP endpoints (like Slack) within the same application.


1. Create a Webhook Signing Key

First, create a new signing key in Peoplelogic. This key is used to sign all webhook payloads.

curl -X POST "https://api.peoplelogic.dev/api/v1/webhook-key"      -H "Authorization: Bearer <YOUR_API_TOKEN>"      -H "Content-Type: application/json"      -d '{"name":"MySigningKey","default":true}'

Sample response:

{
  "id": "f6a1c9b2-0000-1111-2222-aaaaaaaaaaaa",
  "name": "MySigningKey",
  "secretBase64": "YWJjMTIzIT8kKiYoKSctPUB+",
  "default": true,
  "createdAt": "2025-05-28T14:21:00Z"
}

Note: Keep the secretBase64 value secure. You’ll use it in your listener to verify incoming webhook signatures.


2. Register a Webhook for Objective Status Changes

Now, register the webhook endpoint in Peoplelogic:

curl -X POST "https://api.peoplelogic.dev/api/v1/webhook"      -H "Authorization: Bearer <YOUR_API_TOKEN>"      -H "Content-Type: application/json"      -d '{
           "eventType": "objective.patch",
           "ifUpdates": ["status"],
           "url": "https://your-server.com/webhooks/peoplelogic",
           "signingKey": "f6a1c9b2-0000-1111-2222-aaaaaaaaaaaa"
         }'

In this request:

  • url is your public webhook endpoint.

  • ifUpdates specifies which fields must be present in the change for the webhook to fire.

  • signingKey is the ID of the key created above.

Sample response:

{
  "id": "a9b8c7d6-3333-4444-5555-bbbbbbbbbbbb",
  "eventType": "objective.patch",
  "ifUpdates": ["status"],
  "url": "https://your-server.com/webhooks/peoplelogic",
  "signingKey": "f6a1c9b2-0000-1111-2222-aaaaaaaaaaaa",
  "active": true,
  "createdAt": "2025-05-28T14:22:00Z"
}

This webhook fires whenever an objective’s status changes, thanks to the ifUpdates filter.


3. Create and Complete an OKR Objective

Next, create a demo OKR objective:

curl -X POST "https://api.peoplelogic.dev/api/v1/objective"      -H "Authorization: Bearer <YOUR_API_TOKEN>"      
-H "Content-Type: application/json"      
-d '{
           "name": "Improve Customer Satisfaction",
           "description": "Boost our NPS score",
           "startingValue": 0,
           "targetValue": 100
         }'

You may need to adjust fields such as dates or cycle IDs depending on your Peoplelogic org configuration.

Sample response:

{
  "id": "d9e8f7a6-6666-7777-8888-cccccccccccc",
  "name": "Improve Customer Satisfaction",
  "description": "Boost our NPS score",
  "status": { "current": "ACTIVE", "changedAt": null },
  "active": true
}

Save the id from the response—let’s call it OBJ_ID.

Now complete the objective:

curl -X PATCH "https://api.peoplelogic.dev/api/v1/objective/OBJ_ID"      
-H "Authorization: Bearer <YOUR_API_TOKEN>"      
-H "Content-Type: application/json"      
-d '{"status":"COMPLETED"}'

Sample response:

{
  "id": "d9e8f7a6-6666-7777-8888-cccccccccccc",
  "status": { "current": "COMPLETED", "changedAt": "2025-05-28T14:25:00Z" },
  "active": true
}

When you issue the PATCH, Peoplelogic sees the status field changed and publishes an EntityEvent of type "objective.patch". Since your webhook is listening for "objective.patch" with status in ifUpdates, Peoplelogic will send a POST to your Ktor server’s /webhooks/peoplelogic endpoint.


Example API Output Summary

Putting it all together, you'll get something similar to the following:

# 1. Create Signing Key
curl -X POST "https://api.peoplelogic.dev/api/v1/webhook-key" -d '{"name":"MyKey"}'
# (Response) 
{
  "id": "f6a1c9b2-...-1234",
  "name": "MyKey",
  "secretBase64": "YWJjMTIzIT8kKiYoKSctPUB+",
  "default": true,
  "createdAt": "2025-05-28T14:21:00Z"
}

# 2. Register Webhook
curl -X POST "https://api.peoplelogic.dev/api/v1/webhook" -d '{
  "eventType":"objective.patch","url":"https://myapp.com/webhooks/peoplelogic",
  "ifUpdates":["status"],"signingKey":"f6a1c9b2-...-1234"
}'
# (Response)
{
  "id": "a9b8c7d6-...-4321",
  "eventType": "objective.patch",
  "url": "https://myapp.com/webhooks/peoplelogic",
  "ifUpdates": ["status"],
  "signingKey": "f6a1c9b2-...-1234",
  "active": true,
  "createdAt": "2025-05-28T14:22:00Z"
}

# 3. Create Objective
curl -X POST "https://api.peoplelogic.dev/api/v1/objective" -d '{
  "name": "Improve CSAT","description": "Boost score","targetValue":100,"startingValue":0
}'
# (Response)
{
  "id": "d9e8f7a6-...-7890",
  "name": "Improve CSAT",
  "description": "Boost score",
  "status": {"current": "ACTIVE", ...},
  "active": true,
  ...
}

# 4. Complete Objective
curl -X PATCH "https://api.peoplelogic.dev/api/v1/objective/d9e8f7a6-...-7890" -d '{"status":"COMPLETED"}'
# (Response)
{
  "id": "d9e8f7a6-...-7890",
  "status": {"current": "COMPLETED", "changedAt": "2025-05-28T14:25:00Z", ...},
  ...
}

4. Ktor Webhook Listener

Finally, let’s set up a Ktor server to receive and handle the webhook:

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.http.*
import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.util.Base64

fun main() {
    val secretBase64 = "YWJjMTIzIT8kKiYoKSctPUB+"  // from step 1
    val slackWebhookUrl = "<YOUR_SLACK_WEBHOOK_URL>"

    embeddedServer(Netty, port = 8080) {
        routing {
            post("/webhooks/peoplelogic") {
                val payload = call.receiveText()
                val signature = call.request.headers["X-Signature"] ?: ""

                // Verify HMAC-SHA256 signature
                val secretBytes = Base64.getDecoder().decode(secretBase64)
                val hmac = Mac.getInstance("HmacSHA256").apply {
                    init(SecretKeySpec(secretBytes, "HmacSHA256"))
                }.doFinal(payload.toByteArray())
                val expected = Base64.getEncoder().encodeToString(hmac)
                if (signature != expected) {
                    call.respond(HttpStatusCode.Unauthorized, "Invalid signature")
                    return@post
                }

                // Parse payload
                val json = Json.parseToJsonElement(payload).jsonObject
                val objId = json["id"]?.jsonPrimitive?.content ?: "unknown"
                val status = json["status"]?.jsonObject
                              ?.get("current")?.jsonPrimitive?.content

                // Notify Slack if completed
                if (status == "COMPLETED") {
                    val client = HttpClient {
                        install(ContentNegotiation) { json() }
                    }
                    client.post(slackWebhookUrl) {
                        contentType(ContentType.Application.Json)
                        setBody(JsonObject(mapOf(
                            "text" to JsonPrimitive("✅ Objective $objId completed!")
                        )))
                    }
                }
                call.respondText("OK")
            }
        }
    }.start(wait = true)
}

Point your Peoplelogic webhook at https://your-server.com/webhooks/peoplelogic, and you’re all set. When an objective is completed, Peoplelogic will call your endpoint, you’ll verify the signature, and your app will post a Slack message. Happy coding!

PreviousManaging Meetings with Kotlin and OkHttp

Last updated 2 days ago

eventType is the Peoplelogic event (format is "<entityType>.<changeType>", e.g. objective.patch for updates). Using "objective.patch" with ifUpdates: ["status"] means “notify me when any objective is patched and its status field changed.” This filtering behavior is handled by Peoplelogic’s webhook triage (see ).

GitHub