Moderation queue & enforcement.
Two lanes. The customer (PAK) lane files flags and polls its own report status. The admin (workspace) lane works the queue, acts directly on anything — flagged or not — and reaches every target kind: posts, comments, actors, and direct messages. Every enforcement writes an auditable moderation_action row and fires a webhook.
1
File a flag (customer lane)
A flag is a report from one actor against a target. Four target kinds: post, comment, actor, and direct_message. A direct_message flag requires the reporter to be a participant in that conversation — you can only report a DM you can see. Flags are idempotent per (reporter, target) while open, so a retry de-duplicates onto the existing open flag rather than stacking. Enough distinct reporters on one target trips the community's auto-hide threshold and hides a post/comment as a side effect (auto_hidden: true in the response).
# Flag a post
curl -X POST -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" \
-d '{"reporter_actor_id":"<me>","target_kind":"post","target_id":"<post>","reason":"spam"}' \
"https://social.productcraft.co/v1/communities/<c>/flags"
# Flag a direct message (reporter must be a participant → else 403)
curl -X POST -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" \
-d '{"reporter_actor_id":"<me>","target_kind":"direct_message","target_id":"<message>","reason":"harassment"}' \
"https://social.productcraft.co/v1/communities/<c>/flags"2
Poll your own report status (customer lane)
Reporters answer “was my report reviewed?” through a listing scoped to a required reporter_actor_id. The customer lane never sees other reporters' flags — only the caller's own. The listing is cursor-paginated newest-first and filterable by status (open / dismissed / actioned).
curl -H "Authorization: Bearer pcft_live_..." \
"https://social.productcraft.co/v1/communities/<c>/flags?reporter_actor_id=<me>&status=actioned"3
Work the queue (admin lane)
Moderators list open flags and act on one. Acting closes the flag (dismissed or actioned) and applies the enforcement to the flagged target — a post/comment transitions status, a direct message is moderator-soft-deleted, an actor is suspended or reinstated. Once a flag is closed, a second punitive action on it is a 409 FLAG_ALREADY_CLOSED; only the reversals (unhide / restore / dismiss) stay legal, so a hide can still be undone after the fact.
# List the open queue
curl -H "Authorization: Bearer pcft_live_..." \
"https://social.productcraft.co/v1/workspaces/<ws>/communities/<c>/moderation/flags?status=open"
# Act on a flag (removes the post, closes the flag as actioned)
curl -X POST -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" -H "Idempotency-Key: <uuid>" \
-d '{"action":"remove","notes":"Confirmed spam."}' \
"https://social.productcraft.co/v1/workspaces/<ws>/communities/<c>/moderation/flags/<flag>/actions"4
Act directly, no flag required (admin lane)
A moderator can enforce against anything — including content nobody reported — with a direct action. This writes a moderation_action audit row with flag_id: null (an unsolicited action) and fires moderation.action.applied. Target kinds and their effects:
post/comment— status transition (hide/remove/unhide/restore).direct_message— moderator soft-delete (bypasses the sender-only gate).actor—bansuspends the actor;restorereinstates them. This is an auditable wrapper over the actorstatus=suspendedflag, not a separate mechanism.
warn is audit-only across every target kind — it records and fires the webhook without changing state.
# Suspend an actor directly (no preceding flag)
curl -X POST -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" -H "Idempotency-Key: <uuid>" \
-d '{"target_kind":"actor","target_id":"<actor>","action":"ban","notes":"Ban evasion."}' \
"https://social.productcraft.co/v1/workspaces/<ws>/communities/<c>/moderation/actions"
# Soft-delete a direct message
curl -X POST -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" -H "Idempotency-Key: <uuid>" \
-d '{"target_kind":"direct_message","target_id":"<message>","action":"remove"}' \
"https://social.productcraft.co/v1/workspaces/<ws>/communities/<c>/moderation/actions"5
Webhooks
Every step feeds the moderation webhook catalog so an external mirror can track the queue in real time: flag.created when a report lands, flag.updated when its status changes, and moderation.action.applied for every enforcement — including direct actions, where flag_id is null. Subscribe on the webhooks surface.