Discord Webhook Mentions — allowed_mentions, @everyone, Roles & Users
Control who gets pinged by your Discord webhooks. Master allowed_mentions to safely ping users, roles, and @everyone — with working code in Python, JavaScript and curl.
By default, Discord webhooks ping everyone mentioned in the content. That sounds reasonable — until your monitoring webhook accidentally @everyones a 50,000-member guild because the alert text contained the word “everyone” wrapped in <@&>. The fix is allowed_mentions, a small but critical part of every webhook payload.
This guide covers exactly what allowed_mentions does, how each mode behaves, and the patterns to ping safely from Python, JavaScript and curl.
What allowed_mentions Controls
When Discord parses your message content for mentions like <@123>, <@&456>, or @everyone, it can do one of two things:
- Render them as text only (no notification, no highlight)
- Actually ping the user/role/everyone
allowed_mentions tells Discord which mentions in the message are allowed to ping. Anything not listed becomes plain text.
Default Behavior (DANGEROUS)
If you omit allowed_mentions entirely:
{ "content": "<@&123456789> deploy is broken" }
Discord pings the role. And if you have @everyone somewhere in your content, that pings too. This is the most common cause of “oops, my CI just woke up the entire server at 3 AM.”
Safe Default: Ping Nobody
The safest pattern — opt out of all pings unless explicitly approved:
{
"content": "Server warning <@&999>",
"allowed_mentions": { "parse": [] }
}
parse: [] means “don’t auto-parse any mentions”. The role mention renders as a clickable name, but no notification fires.
The parse Array
parse accepts up to three values: "users", "roles", "everyone".
{
"content": "Hey @everyone, deploy is live",
"allowed_mentions": { "parse": ["everyone"] }
}
This does ping @everyone (and @here). Use ["users"] to ping any user mention but no roles, etc.
Pinging Specific Users Only
parse is a blunt tool. For surgical pings, list IDs in users and leave users out of parse:
{
"content": "<@111> <@222> please review",
"allowed_mentions": {
"parse": [],
"users": ["111"]
}
}
Only user 111 gets pinged. User 222 shows as a clickable mention but no notification.
Important: if you put
"users"inparseAND specifyusers: ["111"], Discord raises a 400. The two are mutually exclusive — pick one approach per call.
Pinging Specific Roles Only
Same idea for roles:
{
"content": "<@&100> <@&200> on call!",
"allowed_mentions": {
"parse": [],
"roles": ["100"]
}
}
Only role 100 is notified.
Python — Helper Function
import requests
WEBHOOK_URL = "https://discord.com/api/webhooks/ID/TOKEN"
def send(content: str, ping_users=None, ping_roles=None, ping_everyone=False):
"""Send with explicit, safe mention rules."""
parse = []
payload = {"content": content, "allowed_mentions": {"parse": parse}}
if ping_everyone:
parse.append("everyone")
if ping_users:
payload["allowed_mentions"]["users"] = [str(u) for u in ping_users]
if ping_roles:
payload["allowed_mentions"]["roles"] = [str(r) for r in ping_roles]
r = requests.post(WEBHOOK_URL, json=payload, timeout=10)
r.raise_for_status()
# Examples
send("Daily report posted", ) # No pings
send("<@123> code review please", ping_users=[123]) # Ping one user
send("<@&999> incident!", ping_roles=[999]) # Ping one role
send("@everyone maintenance window", ping_everyone=True) # Ping everyone (use sparingly)
This makes “ping nobody” the default and forces you to explicitly opt in.
JavaScript — Same Pattern
async function send(content, { users = [], roles = [], everyone = false } = {}) {
const allowed_mentions = { parse: [] };
if (everyone) allowed_mentions.parse.push('everyone');
if (users.length) allowed_mentions.users = users.map(String);
if (roles.length) allowed_mentions.roles = roles.map(String);
const res = await fetch(process.env.WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content, allowed_mentions }),
});
if (!res.ok) throw new Error(`Send failed: ${res.status}`);
}
await send('<@&999> incident!', { roles: ['999'] });
curl
curl -H 'Content-Type: application/json' \
-d '{
"content": "<@123> review please",
"allowed_mentions": { "parse": [], "users": ["123"] }
}' \
https://discord.com/api/webhooks/ID/TOKEN
Mention Syntax Cheat Sheet
| Goal | Format in content |
|---|---|
| Mention a user | <@USER_ID> |
| Mention a role | <@&ROLE_ID> |
| Mention @everyone | @everyone |
| Mention @here | @here |
| Channel link | <#CHANNEL_ID> |
User and role IDs are 17–19 digit numbers. Right-click a user/role with Developer Mode enabled → “Copy ID”.
The replied_user Flag
If your webhook replies to another message via message_reference, replied_user controls whether the original author is pinged:
{
"content": "Got it",
"message_reference": { "message_id": "1234" },
"allowed_mentions": { "replied_user": false }
}
Most automation should set replied_user: false unless you specifically want to alert the person you’re replying to.
Limits
- Max 100 user IDs in
users - Max 100 role IDs in
roles - IDs must be strings in JSON (Discord IDs are 64-bit, JS numbers lose precision)
parseandusers/rolescannot overlap (400 error)
Patterns That Avoid Mistakes
Pattern 1: Allowlist of role IDs. Hardcode a small set of roles your webhook is allowed to ping; reject any others.
ALLOWED_ROLES = {"on-call-eng", "ops-lead"}
ROLE_IDS = {"on-call-eng": "111", "ops-lead": "222"}
def page(role_name: str, message: str):
if role_name not in ALLOWED_ROLES:
raise ValueError(f"Pinging '{role_name}' not allowed from this webhook")
rid = ROLE_IDS[role_name]
send(f"<@&{rid}> {message}", ping_roles=[rid])
Pattern 2: Strip @everyone from user-supplied input. If your webhook re-emits user messages, replace mentions before sending:
import re
def safe(text: str) -> str:
text = text.replace("@everyone", "@\u200beveryone")
text = text.replace("@here", "@\u200bhere")
return re.sub(r"<@&?\d+>", lambda m: m.group(0).replace("<", "<\u200b"), text)
A zero-width space breaks the mention parser without visibly changing the text.
Pattern 3: Dry-run mode. When testing in production channels, force allowed_mentions: { parse: [] } regardless of caller intent. Saves you from the 3 AM mistake.
Common Errors
| Error | Cause |
|---|---|
400 allowed_mentions: cannot have both parse and users/roles | You set "users" in parse AND populated users array |
| Mention shows but no notification | Expected when allowed_mentions.parse: [] and ID not listed |
@everyone pings even with safe config | mention_everyone permission missing on the webhook’s role — webhook can’t ping it |
Test in the Builder
The Discord Webhook builder renders allowed_mentions configuration visually — toggle “Ping users”, “Ping roles”, “Ping @everyone” before sending so you see exactly what notifications will fire. Helpful when setting up notifications or automating alerts.
References
- Discord docs: Allowed Mentions Object
- Discord docs: Message Formatting
Try it in our tool
Open Discord Webhook Builder