One @RestController — signed articles arrive as JSON.
Spring renders the blog; DraftSEO delivers the posts. The recipe is a @RestController with a @PostMapping — binding @RequestBody String rawBody hands you the exact body for the HMAC, which javax.crypto.Mac computes and MessageDigest.isEqual compares. Jackson (bundled with Spring Boot Web) then reads the payload.
| Connection | A @RestController — @PostMapping("/webhook"), 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 | Any Spring Boot app — @RequestBody String gives the raw body; no extra deps |
Who does what
Add the @RestController, verify the HMAC over @RequestBody String rawBody, then save the entity with JPA. After the green test, zero effort per article.
Your repositories and view layer stay entirely yours.
Setup
Connect a site → Spring (Java). You get a signing secret and the controller recipe.
~1 minute
Paste the @PostMapping handler, bind @RequestBody String rawBody, verify the signature and save the entity — the recipe covers it.
~10 minutes
DraftSEO sends a signed test event; a green check confirms the connection.
~1 minute
Every generated article is delivered to your controller 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 Jackson reads as a JsonNode or maps onto your DTO.
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.
Bind @RequestBody String rawBody — Spring hands you the exact request body as a String before any object mapping, which is what the HMAC is computed over. If you use a filter or ContentCachingRequestWrapper instead, make sure it exposes the same untouched bytes.
No card required. Signed test delivery in under a minute.