Moderation
The MisarBlog reporting flow and the admin moderation queue — a unified view of content reports and comment flags with audit-logged actions.
MisarBlog lets any signed-in reader report content, and gives platform admins a single queue to review reports and comment flags and act on them. Every moderator action is audit-logged.
These are first-party MisarBlog web-app endpoints — session-authenticated and CSRF-protected. The admin queue is additionally gated to platform admins. They are not part of the public API-key surface (api.misar.io/blog/v1/*). Paths are relative to the app origin (www.misar.blog).
Reporting content
Any signed-in reader can report an article, series, or profile.
/api/moderation/reportFiles a content report. On success, moderators and the content owner are notified via the content_reported notification (fire-and-forget). Rate-limited to 20 reports / hour per user+IP. A user can report the same content only once (a duplicate returns 400).
Request body
content_typestringbodyrequiredOne of article, series, profile.
content_idstring (UUID)bodyrequiredThe reported content's id.
reasonstringbodyrequiredOne of spam, misinformation, harassment, adult_content, hate_speech, copyright, other.
detailsstringbodyOptional free-text context, ≤ 500 characters.
Response fields
successbooleantrue when the report is recorded.
curl -X POST "https://www.misar.blog/api/moderation/report" \
-H "Content-Type: application/json" \
-H "x-csrf-token: <token>" \
-H "Cookie: <session>" \
-d '{
"content_type": "article",
"content_id": "550e8400-e29b-41d4-a716-446655440000",
"reason": "spam"
}'{ "success": true }Admin moderation queue
Platform admins review a unified queue that merges content reports and comment flags (flags grouped per comment with a count).
/api/moderation/adminReturns the merged moderation queue, newest first. Admin-only. Rate-limited to 120 requests / hour.
Query parameters
statusstringquerydefault: pendingFilter: pending, resolved, or all.
pagenumberquerydefault: 1Page number.
limitnumberquerydefault: 50Page size (max 100).
Response fields
itemsQueueItem[]Each item carries source (content_report | comment_flag), content_type, content_id, reason, status, created_at, the reporter, resolved context (label + URL), and flag_count for grouped comment flags.
totalnumberTotal items across all pages for the filter.
pagenumberEchoed page number.
limitnumberEchoed page size.
curl "https://www.misar.blog/api/moderation/admin?status=pending&page=1&limit=50" \
-H "Cookie: <admin-session>"{
"items": [
{
"source": "content_report",
"id": "…",
"content_type": "article",
"content_id": "…",
"reason": "spam",
"status": "pending",
"created_at": "2026-07-26T10:00:00Z",
"reporter": { "username": "alice", "display_name": "Alice" },
"context": { "label": "Ship faster", "url": "https://www.misar.blog/@you/articles/ship-faster" }
}
],
"total": 1,
"page": 1,
"limit": 50
}Taking action
/api/moderation/adminPerforms a moderation action. Admin-only, CSRF-protected, and audit-logged to admin_audit_log (SOC 2 CC6.1).
Request body
actionstringbodyrequiredOne of the actions below.
target_idstring (UUID)bodyrequiredThe report id (for *_report), comment id (for comment actions and resolve_flags), or article id (for *_article).
redact_textstringbodyOptional replacement text for redact_comment (≤ 500 chars). Defaults to [Removed by moderator].
Actions
resolve_report / dismiss_reportreportMark a content report reviewed or dismissed.
resolve_flagscommentResolve all open flags on a comment.
hide_comment / unhide_commentcommentHide or unhide a comment.
redact_commentcommentReplace a comment's text with redaction text.
delete_commentcommentDelete a comment.
archive_article / unarchive_articlearticleArchive (hide from public) or restore an article.
curl -X POST "https://www.misar.blog/api/moderation/admin" \
-H "Content-Type: application/json" \
-H "x-csrf-token: <token>" \
-H "Cookie: <admin-session>" \
-d '{ "action": "archive_article", "target_id": "550e8400-e29b-41d4-a716-446655440000" }'{ "ok": true }Status codes
- 401 — not signed in.
- 403 — invalid CSRF token, or the caller is not a platform admin.
- 400 — invalid body / unknown action / bad
target_id. - 429 — rate limit exceeded (
Retry-Afterheader included). - 500 — action failed.