Documentation

Moderation and blocking

Block users, report content, and the moderation hooks behind them.

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.

POST/community/blocksJWT
DELETE/community/blocks/:userIdJWT
GET/community/blocksJWT

The 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: getFeed filters 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.

POST/community/reportsJWT
await 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.