Community ships member-driven moderation primitives: a customer can block another customer, and they can report a post, comment, page, or user. Both surface as records that admin staff act on through the /community/* controller and downstream automations.
Blocking
A block is a one-way relationship from the caller to a target email. Once active, it suppresses feed visibility and DM delivery in both directions, and also hides reactions/comments from the blocker's UI.
/community/blocksJWT/community/blocks/:userIdJWT/community/blocksJWTThe same routes are mirrored under /client/community/blocks/* for customer JWTs.
// Block someone
await fetch('/community/blocks', {
method: 'POST',
headers: {
orgid: 'my-org',
Authorization: `Bearer ${jwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
targetId: '[email protected]',
reason: 'spam', // optional, free-form
}),
});
// Unblock
await fetch('/community/blocks/[email protected]', {
method: 'DELETE',
headers: { orgid: 'my-org', Authorization: `Bearer ${jwt}` },
});
// List blocks
const { data } = await fetch('/community/blocks', {
headers: { orgid: 'my-org', Authorization: `Bearer ${jwt}` },
}).then(r => r.json());
How blocks are enforced
- Feed:
getFeedfilters out posts authored by anyone in the caller's blocked list — and posts where the caller is blocked by the author. - Direct messages:
sendMessage(REST and WS) returns an error if either party blocks the other; the DM is not persisted. - Connection/follow: existing edges are not deleted, but new connection requests fail and follow records are filtered from list endpoints.
- Group chat: blocked users still share group rooms — the block is between individuals, not between users in shared spaces. Use group member removal for that.
Reporting
A report flags content or a user for staff review. Anyone signed in can submit one.
/community/reportsJWTawait fetch('/community/reports', {
method: 'POST',
headers: {
orgid: 'my-org',
Authorization: `Bearer ${jwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
target: 'post-id-or-email',
targetType: 'post', // 'post' | 'comment' | 'page' | 'user' | 'message' | 'story'
reason: 'harassment',
description: 'Repeated personal attacks in comments.',
}),
});
Reports are persisted as community_report records with status pending. They surface in the staff inbox (CRM /crm/inbox/*) and trigger the standard automation event for "report submitted", which you can route to a moderator queue, a webhook, or an AI triage step.
Admin moderation hooks
Moderation actions are handled with the existing data layer rather than a dedicated controller — staff CRUD community_post, community_comment, community_story, and community_page records directly via /data/{collection}/*. Two specific transitions emit events the rest of the system listens for:
- Setting a post or comment to
state: 'removed'triggers a notification template targeted at the original author (community-content-removed). - Setting a customer record to
state: 'suspended'propagates to all customer-scoped endpoints; the suspended customer can sign in but receives an empty feed and cannot post.
Building a moderation queue
To build a reviewer dashboard, query reports by status:
const pending = await fetch('/data/community_report/list?status=pending&limit=50', {
headers: { orgid: 'my-org', Authorization: `Bearer ${jwt}` },
}).then(r => r.json());
Then resolve each by setting state to actioned or dismissed on the report record and applying the corresponding action to the underlying target.
There is no first-class moderation controller in the current build — the data-layer + automation combination is intentional. If you need a workflow-style review pipeline, wire it up in the Automation module with community_report.created as the trigger.
Privacy considerations
- A blocked user is not notified. The block is invisible from their side except that messages and connection requests silently fail.
- A reporter's identity is included on the report record. Mask it in any reviewer UI you build if your policy promises anonymity.
- Blocks and reports are first-class records — they're included in customer data exports under GDPR-style requests.