Skip to main content
All articles
· Updated April 21, 2026

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.

mentionsallowed_mentionseveryonepingrolesuserswebhookdiscord api
Discord Webhook Mentions — allowed_mentions, @everyone, Roles & Users

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:

  1. Render them as text only (no notification, no highlight)
  2. 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" in parse AND specify users: ["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

GoalFormat 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)
  • parse and users/roles cannot 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

ErrorCause
400 allowed_mentions: cannot have both parse and users/rolesYou set "users" in parse AND populated users array
Mention shows but no notificationExpected when allowed_mentions.parse: [] and ID not listed
@everyone pings even with safe configmention_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