Skip to main content

Moderation

Cortex turns voice into text, and text is something you can moderate. Moderation in Cortex has two halves:

  1. Plugins analyze the transcript and produce annotations (e.g. a toxicity score on a message).
  2. Sanctions record the moderation actions you take in response (warn, mute, ban…), with a complete audit trail.

Plugins & annotations

A plugin is a first-party extension that reacts to domain events and attaches structured output. The classic example: a toxicity/hate-speech plugin that runs on every message.created and annotates messages it flags.

You don’t write plugin code — you activate plugins for a project and configure them. (For your own logic, use serverless functions.)

// Browse the catalog
const plugins = await client.pluginCatalog.list();

// Activate one for your project with settings
await project.plugins.install({
pluginId: "toxicity",
settings: { /* per-plugin configuration */ },
});

Plugin output is stored as annotations:

  • Message annotations — attached to a single message (e.g. toxicity, sentiment, language). Emit message.annotation.created.
  • Session annotations — attached to a whole session (e.g. a summary or action_items). Emit session.annotation.created.

The annotation body is free-form and plugin-defined, carried under a content object on the event — so you can react to exactly the fields a plugin produces.

// React to flagged content as it happens
project.sessions
.get(sessionId)
.then((s) => s.watchMessages())
.then((sub) => sub.onSnapshot((messages, changes) => {
// inspect annotations on new messages and decide whether to sanction
}));

Sanctions

A sanction is a recorded moderation action. Sanctions can be created by a moderator, by a plugin that detected a violation, or directly via the API from your game backend. Every sanction is audit-logged: who created it, when, why, and — if lifted — who revoked it and when.

const sanction = await project.sanctions.create({
participantId: participant.id,
type: "mute",
reason: "Repeated harassment",
});

// later
await sanction.revoke({ reason: "Appeal upheld" });

Sanction types

Cortex supports a graduated set of actions so you can match the response to the offense:

TypeEffect
warnRecord-only warning, no participation impact
muteAudio muted
listen_onlyCan hear but not speak
text_onlyVoice disabled, text still allowed
shadow_muteMuted without notifying the user
temp_ban / perm_banBlocked for a window / permanently
human_reviewFlagged for a moderator, no user-visible effect

(Game-specific variants such as ranked restrictions and matchmaking delays also exist.)

Cross-session bans

To ban someone everywhere, key the sanction on externalUserId rather than a single participant row. Your enforcement code can then look up active sanctions by user id on every join — even across sessions and even if the participant record changes.

const active = await project.sanctions.getActive("user-123");
Cortex records; you enforce

Cortex is the system of record for moderation decisions — it stores them and emits events. It does not itself kick people out of rooms. Your game server, voice layer or app reads the sanction records (via the API, a webhook, or a function) and enforces them.

Audit trail

Revoked sanctions are never deleted — the full history (createdBy, createdAt, revokedBy, revokedAt, reason) is retained for accountability and dispute resolution.

A typical moderation loop