One Ktor route — signed articles arrive as JSON.
Your Kotlin service owns the rendering; DraftSEO delivers the content. The recipe is a Ktor post("/webhook") route — call.receiveText() gives you the raw body for the HMAC, which javax.crypto.Mac computes and MessageDigest.isEqual compares before you store the post. Ktor with ktor-server-core and ktor-server-netty is all it needs.
| Connection | A Ktor route — post("/webhook") in Application.kt, recipe provided |
|---|---|
| Security | HMAC-signed + timestamped — MessageDigest.isEqual over ${timestamp}.${rawBody}, replay-window protected |
| Payload | Full article JSON — title, HTML, images, meta, slug, language |
| Images | DraftSEO CDN URLs — URLs ride in the payload; rehost to your own storage if you prefer |
| Framework fit | Ktor (or Spring) — receiveText() for the raw body; the same check ports to Spring Boot |
Who does what
Add the Ktor route, verify the HMAC over call.receiveText(), then persist the post with your DB layer. After the green test, zero effort per article.
Your Exposed or JPA, and your rendering, stay yours.
Setup
Connect a site → Kotlin. You get a signing secret and the Ktor recipe.
~1 minute
Add ktor-server-core and ktor-server-netty, define post("/webhook"), read receiveText(), verify the signature and store the post.
~10 minutes
DraftSEO sends a signed test event; a green check confirms the connection.
~1 minute
Every generated article is delivered to your Ktor route on cadence.
Ongoing · zero clicks
FAQ
A JSON body { event, deliveryId, data } — data holds the title, HTML article, slug, language, meta fields and image URLs, which kotlinx.serialization or Jackson parses into your model.
Each request is HMAC-signed over ${timestamp}.${rawBody} with a timestamp header; you recompute it with javax.crypto.Mac (HmacSHA256) and compare using MessageDigest.isEqual, rejecting anything past the 5-minute window.
The recipe is Ktor (call.receiveText() for the raw body), but the verification is plain JDK crypto, so it ports unchanged to a Spring Boot @RestController if that is your stack — bind @RequestBody String and run the same HMAC check.
No card required. Signed test delivery in under a minute.