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
| event | When |
|---|---|
moderation.flagged | monitor 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.resolved | A moderator approved/rejected a queue item. |
report.escalated | The number of reports exceeded the threshold — the content was sent to the queue. |
ping | Test 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.