Skip to content

Member moderation workflow

Member moderation uses one reviewed action at a time and has no immediate-call path. Set capabilities.administration: true, list every eligible guild in scopes.adminGuildIds, and list the bot operators, service accounts, break-glass accounts, or other ineligible targets in scopes.protectedUserIds. The administration guild allowlist must be a subset of readScope.guildIds when the read allowlist is present.

Supported actions are kick, ban, timeout, remove-timeout, and unban. Ban accepts deleteMessageSeconds from 0 through 604800 and defaults to 0. Timeout requires durationMinutes from 1 through 40319, staying conservatively below Discord's 28-day limit. Every action requires a non-blank Discord audit-log reason whose URL-encoded form fits Discord's 512-character limit.

  1. Choose a unique one-shot operation key, then call plan_member_moderation with it, the exact guild ID, user ID, action, audit reason, and action parameters.
  2. Review the pinned application and bot IDs, exact target ID and untrusted profile preview, current member, ban, or timeout state, complete effective permission evidence, role positions, parameters, reason, risks, warnings, operation-key hash, verification boundary, and keyed digest.
  3. Call execute_member_moderation with identical inputs plus the digest.
  4. Approve the signed MCP confirmation only if the exact target, action, parameters, reason, operation-key hash, and digest remain intended.
  5. Review the returned activity ID, observed exact state, and verification outcome before attempting any follow-up. Never reuse a reserved operation key.

Planning verifies the guild owner, current connector bot membership, complete guild roles, the exact target identity, and the action's current state. KICK_MEMBERS is required for kick, BAN_MEMBERS for ban and unban, and MODERATE_MEMBERS for timeout changes unless the bot has ADMINISTRATOR, which is still discouraged. For actions against a current member, the bot's highest role must be strictly above the target's highest role. The guild owner, the connector bot, configured protected IDs, and administrators targeted by timeout actions are rejected.

Kick, timeout, and timeout removal require a current exact member. Ban accepts a current member or an exact Discord user outside the guild, but rejects an existing ban. Unban requires an existing exact ban, and timeout removal requires a currently active timeout. Missing roles, duplicate or invalid role evidence, unknown member role IDs, mismatched Discord response identities, and equal role positions all fail closed.

The plan digest is process-keyed and covers the exact application, bot, guild, and user IDs, domain-separated operation-key hash, action, audit reason, numeric parameters, guild owner, relevant bot and target roles, effective permission bitfields, current membership, ban, and timeout state, and the declared privacy and verification boundaries. Display names and avatars do not affect freshness. Timeout plans bind the reviewed duration rather than an early wall-clock expiration; execution calculates the final expiration after approval. A connector restart invalidates outstanding digests.

Immediately before mutation, the service rebuilds the complete plan and requires the same digest. The production facade first acquires a durable claim over the exact member target across connector processes sharing the activity-state root. Execution atomically reserves the one-shot operation key, then writes pending activity containing only exact IDs, the domain-separated key hash, action, digest, numeric parameters, timestamps, fixed outcomes, verification, and sanitized error categories. Audit reasons, usernames, nicknames, role names, avatars, raw operation keys, Discord payloads, and transport causes are never persisted.

Each kick, ban, unban, timeout, or timeout removal is dispatched once with automatic rate-limit retry disabled and no automatic rollback. Kick readback requires exact member absence, ban and unban require exact ban presence or absence, and timeout changes require both the mutation response and a fresh exact-member read to match the final expiration. A successful Discord response with mismatching fresh state is returned as completed-with-drift. A known non-rate-limit Discord 4xx refusal before acknowledgement is failed; a rate limit, transport or server failure, malformed mutation response, readback failure, or other indeterminate post-reservation boundary is uncertain and may have completed. Every reserved key remains spent, and an uncertain outcome retains the exact-member claim for operator review. Inspect the member or ban state and Discord audit log before resolving that claim; never retry blindly.

Canonical source: docs/reference.md

Documentation generated for guildcontrol@0.0.0. Canonical source and edit history remain in the public repository. GuildControl is an independent project and is not affiliated with or endorsed by Discord Inc. Discord is used only to identify the platform that GuildControl connects to.