Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Webhooks

Webhooks tell other services about events on the site as they happen: a chat bot announcing uploads, a mirror, or your own scripts. Add them under Admin → Webhooks (for those who manage site settings), with the URL to send to and the events it wants:

EventWhen
post.createda post is uploaded (by any means)
post.approveda pending post is approved
post.deleteda post is deleted or rejected
post.flaggeda post is flagged
comment.createda comment is posted
user.registeredsomeone registers (with a form or single sign-on)

Deliveries

Each event is a POST of JSON:

{
  "id": 42,
  "event": "post.created",
  "created_at": "2026-09-26T12:00:00Z",
  "data": { "post_id": 123, "url": "https://booru.example.com/posts/123", "tags": ["cat"], … }
}

with these headers:

Header
X-Moekura-Eventthe event
X-Moekura-Deliverythe delivery’s number (the same across retries)
X-Moekura-Timestampseconds since 1970 when it was sent
X-Moekura-Signaturesha256= and the HMAC-SHA256, in hex, of {timestamp}.{body} with the webhook’s secret

Check the signature and reject old timestamps (older than five minutes, say), so nobody else can send you events or replay old ones. In Python:

import hashlib, hmac, time

def valid(secret: str, timestamp: str, body: bytes, signature: str) -> bool:
    expected = hmac.new(secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(timestamp)) < 300
    return fresh and hmac.compare_digest("sha256=" + expected, signature)

Answer with any 2xx status. Network errors, 5xx, 408 and 429 are retried in the background, waiting longer each time, for about five hours; other statuses are given up at once. A webhook’s page lists its latest deliveries with what came back, and Send a test sends a ping event. Deliveries are kept for 30 days.

Webhooks don’t follow redirects, and don’t go to private or local addresses unless webhooks.allow_private_addresses is on in the configuration.