API reference

Base URL: https://modwall.dev. All responses are JSON. Machine-readable: openapi.json.

Authentication

The server key in the header of every request:

Authorization: Bearer sk_live_...

You create and revoke keys in the dashboard. It is a secret — keep it server-side, never in frontend code (for the browser, use the widget).

Moderate an image

POST /v1/moderate

Body: multipart/form-data.

FieldTypeDescription
imagefileImage (required). Alias: file.
referencetextAn optional ID of the content on your side — it goes to the HITL queue.
image_urltextOptional preview URL on your infrastructure — for the HITL queue.

Query parameter:

ParamDescription
?threshold=0.8Overrides the block threshold for this request. The decision is then binary (allow/block), with no review band.

200 response:

{
  "scores": { "nsfw": 0.97, "safe": 0.03 },
  "categories": { "nsfw": 0.97, "weapon": 0.12 },   // weapon/violence: BETA, wg profilu
  "blocked_by": ["nsfw"],
  "decision": "block",
  "recommended_action": "block",
  "mode": "enforce",
  "threshold": 0.8,
  "profile": "Domyślny",
  "model_version": "falconsai-int8-1",
  "review_id": 42        // tylko gdy decision = "review"
}
FieldMeaning
decisionThe action for you, per the profile's mode: allow / review / block. In monitor mode it's always allow.
recommended_actionThe model's raw recommendation, independent of mode. In monitor it shows what modwall would do.
categoriesScore per category. nsfw always; weapon / violence (beta) only when you enable them in the key's profile. The strictest enabled category decides (per-category thresholds in the dashboard).
blocked_byCategories that exceeded their block threshold.
profileName of the moderation profile that made the decision (you pin a profile to a key in the dashboard — a different policy per site).
modeThe profile’s active mode: monitor / review / enforce.
model_versionThe model version that computed the score.

We always return raw scores; decision and mode are computed according to your policy. The meaning of the decisions themselves (allow/review/block) and the mode table are in the Policy.

Moderate text

POST /v1/moderate-text

Body: JSON { "text": "…" } (optionally reference, image_url for the queue). Requires the text or both scope on your plan. Multilingual model (understands Polish); returns raw scores and categories.

curl -X POST https://modwall.dev/v1/moderate-text \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"text":"treść do sprawdzenia"}'
{
  "scores": { "unsafe": 0.94, "safe": 0.06 },
  "categories": { "toxic": 0.94 },
  "decision": "block",
  "recommended_action": "block",
  "mode": "enforce",
  "threshold": 0.8,
  "model_version": "xlmr-tox-int8-1"
}

unsafe is the toxicity score (multilingual, understands Polish). categories returns the model’s raw labels (currently toxic; the field is extensible for future models/categories). decision and the modes work exactly as they do for images (monitor/review/enforce).

Report content (reactive layer)

POST /v1/report

Body: reference (required), optionally image_url. Once the report threshold from your policy is exceeded, the content goes to the HITL queue. It does not call the model and does not count toward your limit.

{ "reference": "post-123", "reports": 2, "escalated": true }

Queue item status

GET /v1/review/{id}

Returns the moderator’s decision for a queue item (e.g. by review_id from /v1/moderate).

{ "id": 42, "status": "approved", "reason": "score", "reference": "post-123" }

status: pending / approved / rejected.

Errors

CodeMeaning
400no_image / no_reference — a required field is missing.
401Missing or invalid key.
402quota_exceeded — plan limit exhausted (resets monthly).
403capability_required — your plan doesn't cover this content type (images/text).
413image_too_large — image above the size limit.
429rate_limited — too many requests in a short time.
503inference_unavailable — the engine is temporarily unavailable.

Fail-open: on 503 you decide the fallback behavior — we recommend routing content to manual moderation instead of silently letting it through.