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:
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:
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!
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 ).