Webhooks
Fire-and-wait-for-ack-Benachrichtigungen bei Publish, Unpublish oder Move von Stories — mit Retries, SSRF-Guard und signiertem Payload.
Was triggert einen Webhook
story.published — Publish-Button oder Scheduled-Publish gefeuert.
story.unpublished — Story offline genommen.
story.deleted — Soft-Delete über Admin oder API.
story.moved — Slug geändert (Ordner-Move, Rename).
payload.json
{
"action": "story.published",
"space_id": 1,
"story_id": 42,
"full_slug": "blog/first-post",
"uuid": "c3c7e0f3-...",
"delivery_id": "whd_01HZ...",
"timestamp": "2026-04-23T14:11:02.500Z"
}/api/v1/spaces/{spaceId}/webhooksBearer (Mgmt)Neuen Webhook-Endpoint registrieren.
Request
POST /api/v1/spaces/1/webhooks
Authorization: Bearer sbmgmt_...
{
"name": "Vercel rebuild",
"url": "https://api.vercel.com/v1/deploy-hooks/abc",
"events": ["story.published", "story.unpublished"],
"secret": "<opaque, used for signature>"
}Signatur & Retries
Jeder Request trägt X-Webhook-Signature — HMAC-SHA256 über den Raw-Body mit dem Webhook-Secret. Vor dem Auswerten eines Feldes verifizieren. Failed Deliveries (non-2xx oder Timeout nach 10 s) werden mit Exponential-Backoff bis zu 5-mal wiederholt; danach landet die Delivery als Dead-Letter im Audit-Log.
verify.ts
import { createHmac, timingSafeEqual } from 'node:crypto'
export function verify(rawBody: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(signature, 'hex')
return a.length === b.length && timingSafeEqual(a, b)
}