Webhooks

Instead of polling /v1/review/{id}, you can receive a POST to your URL when a moderator resolves a queue item, when reports escalate content, or — in monitor mode — when the model flags something. You set the URL and secret in the dashboard.

Events

eventWhen
moderation.flaggedmonitor mode: the model flagged content (recommended_action = block/review) even though nothing was blocked. An async "heads-up" for shadow mode — in review/enforce you already have the decision in the response.
review.resolvedA moderator approved/rejected a queue item.
report.escalatedThe number of reports exceeded the threshold — the content was sent to the queue.
pingTest sent from the dashboard.

Payload format

POST {twój_url}
Content-Type: application/json
X-Modwall-Event: moderation.flagged
X-Modwall-Signature: sha256=<hex>

{
  "event": "moderation.flagged",
  "data": {
    "reference": "post-123",
    "recommended": "block",
    "decision": "allow",
    "mode": "monitor",
    "scores": { "nsfw": 0.97, "safe": 0.03 },
    "threshold": 0.8,
    "model_version": "falconsai-int8-1"
  },
  "timestamp": "2026-06-27T12:00:00+00:00"
}

review.resolved carries review_id, status, reference, reason, feedback (model accuracy rating), and resolver.

Signature verification

Compute HMAC-SHA256 of the raw body with your secret and compare it against the X-Modwall-Signature header (constant-time comparison).

import hmac, hashlib

def valid(body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header)

Reject requests with a mismatched signature. Respond with a 2xx code — any other code is marked as a failed delivery (visible in the dashboard). Delivery is asynchronous, with retries (backoff) on an error or a non-2xx response.