Skip to content

Webhooks

Keep webhook inventory behind its own capability gate and exact direct-channel allowlist. The allowlist must remain a subset of any configured read-channel scope. Require a supported guild channel, verified application and bot identities, a complete bounded channel webhook listing, complete guild-role and channel-overwrite evidence, and effective VIEW_CHANNEL plus MANAGE_WEBHOOKS permission before returning an inventory or plan. Parent scope must never grant webhook access to a child thread.

Project Discord webhook responses immediately to exact IDs, type, bounded name, channel and guild IDs, application ID, creator ID, and a creation timestamp derived locally from the webhook snowflake. Reduce the accompanying channel to its exact ID, guild ID, bounded name, parent ID, numeric type, and fixed type name; omit topics, forum metadata, message state, and raw overwrite bodies from the result. Never return a webhook token, execution URL, private credential path, avatar, creator profile, source object, or unknown raw field. Creation must validate the credential-bearing Discord response and pass the token directly into private connector custody before any public service result can be formed. Never add an MCP argument that accepts a token, complete execution URL, credential path, identity override, or raw webhook payload.

Keep creation, change, and deletion behind independent capability gates in addition to webhook audit, and do not add an immediate-call path. Preserve every gate for an actual write: verified identities, exact scope, complete permissions, process-keyed plan binding, signed interactive confirmation, write-aware client approval, final fresh-plan match, durable affected-target coordination, atomic one-shot key reservation, pending activity journaling, one non-retried mutation, strict response validation when a response exists, and fresh complete-inventory readback. Application webhooks remain inventory-only. Channel Follower creation and deletion belong only to the dedicated announcement-subscription workflow with its separate source and target scope.

Creation may add only one strict bounded-name Incoming webhook to a non-full exact channel inventory. Require its plan to bind capacity, the desired name and type, the complete credential-redacted inventory, permission evidence, credential omissions, and durable bearer-capability risks. Require one configured existing canonical process-owned 0700 credential root. After reservation and pending activity, send one non-retried bot-authenticated POST, require a valid token-bearing response, reserve only <webhookId>.token without following links, require a single-link process-owned 0600 regular file, write one bounded newline-terminated UTF-8 token, synchronize the file and containing directory, and verify the created webhook plus every prior inventory member through a fresh complete readback. If Discord identifies the created webhook but private custody fails, return an uncertain content-free orphan result containing the webhook ID so the operator can remediate it; never expose the token or raw storage cause.

Changes may rename, move within one exact guild, or combine both only for one exact Incoming webhook discovered in its asserted current channel. Require both source and destination to be separately allowlisted, supported, non-full where capacity is consumed, and proven through complete inventories and channel-level permissions. Bind exact current and desired metadata, requested and changed fields, both inventories, capacity, credential omissions, and the fact that a move preserves the existing bearer credential while redirecting future external deliveries. Return an exact already-current no-op without confirmation, reservation, activity, or mutation. Otherwise send one non-retried bot-authenticated PATCH and require exact response, destination readback, source absence for a move, and complete non-target inventory evidence.

Deletion must remain limited to one exact Incoming webhook from its asserted complete source inventory. Send one non-retried bot-authenticated DELETE and require a fresh complete inventory proving exact absence. Only after verified Discord absence may the connector inspect and unlink that exact webhook ID's private credential file and synchronize the containing directory. A missing or unconfigured credential is safe; a cleanup failure is local drift and must not obscure the independently proven remote absence. Never remove a credential when the webhook survives or the remote outcome is uncertain. Discord's modify and delete routes are addressed by webhook ID rather than by channel and webhook together. Treat the final inventory-to-mutation interval as an unavoidable external race because Discord supplies no channel-qualified conditional write. Keep MANAGE_WEBHOOKS denied outside exact selected channels, surface the limitation and move consequences in every applicable plan, and require exclusive webhook administration for a high-risk integration.

Keep credential-authenticated webhook message actions behind independent lookup, delivery, edit, and deletion capability gates plus one exact direct text or announcement channel allowlist. Editing and deletion require lookup authority; delivery remains independently selectable. Resolve only an exact webhook ID to an exact private credential file, authenticate one live Incoming webhook, require its guild and channel to match exact bot-readable channel evidence, and reject threads, forum and media parents, voice and Stage channels, direct messages, unsupported webhook types, or parent-derived scope. Keep all credential paths and execution URLs inside the connector.

Exact lookup may return bounded transient untrusted message content and stable identity, time, flag, boolean, and rich-payload count evidence. Omit author profiles, attachment URLs, embed and component bodies, stickers, poll bodies, raw payloads, and unknown fields. Plain-text delivery and editing must accept no username or avatar override, TTS, thread target, forum parameter, embed, component, file, attachment, poll, sticker, raw flag, or raw mention object. Permit only separately allowlisted visible user notifications, disable role and everyone parsing, suppress embeds, use the shared per-channel anti-spam limiter, reserve a stable one-shot intent before one non-retried mutation, validate the exact response, and perform independent exact readback. Reject editing a rich target. Allow a record-free edit no-op only for exact plain-text state with no requested notification.

Webhook message deletion must have no immediate-call path. Bind the verified application and bot, exact guild, direct channel, Incoming webhook, complete projected message content and metadata, transient local review reason, privacy boundary, one-shot operation-key hash, warnings, and every displayed untrusted guild label into a process-keyed plan. Require signed interactive confirmation and rebuild the plan immediately before mutation. Recheck the final webhook target against the reviewed target, coordinate the exact webhook and message, reserve the key, write pending content-free activity, send one non-retried token-authenticated DELETE, and accept absence only from an exact message 404 followed by a still-valid webhook credential pinned to the reviewed guild and channel. Discord accepts no guild audit-log reason on this route, so never send or persist the local review reason. When rich bodies are omitted, bind their displayed counts or presence and explicitly warn that same-count internal rich-payload changes remain outside review evidence.

Persist webhook credentials only in the dedicated exact-ID private files described above. Never persist webhook names, execution URLs, private paths, avatars, creator profiles, source objects, audit-log or local review reasons, message content, notification-user IDs, standalone content hashes, raw operation keys, permission evidence, raw responses, or transport causes. Content-free activity, operation, and coordination records may contain only the exact Discord IDs needed for review, process-keyed plan digest, operation-key hash, timestamps, fixed verification and outcome values, activity ID, sanitized error category, operation kind, and coordination metadata.

A known pre-write Discord 4xx may be classified as failed. Treat rate limiting, transport failure, Discord 5xx, malformed or mismatched success, failed readback, or any otherwise indeterminate post-reservation state as uncertain and potentially completed. Spend every reserved key after every outcome and never retry, roll back, recreate, or compensate automatically. Serialize creation by exact channel and administration changes or deletion by exact webhook inside each process as defense in depth. Serialize private message sends by exact webhook and edits or deletion by exact webhook plus message. The production facade acquires the corresponding durable affected-channel, exact-webhook, exact-message, and guild-webhook-collection claims, so connector processes sharing the activity-state root exclude overlapping writes and retain those claims after uncertainty.

Canonical source: SECURITY.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.