Discord bots can send messages to a channel using a webhook instead of a regular bot message. A webhook message appears to come from a custom username and avatar, not from the bot itself. This is useful when you want a command response to look like it was sent by a specific user or service. Many bot developers want to trigger a webhook when a user runs a slash command. This article explains how to set up a bot that sends a webhook in response to an application command.
Key Takeaways: Sending Webhook Messages from a Discord Bot Command
- discord.js executeWebhook method: Sends a message through an existing webhook object using the webhook ID and token.
- InteractionCreate event: The event your bot listens for to catch slash command executions.
- WebhookClient class: Creates a webhook client that can send messages without fetching the webhook from Discord each time.
What Is a Discord Webhook and Why Use It With a Bot Command
A Discord webhook is a way to send messages to a channel programmatically. Unlike a regular bot message that shows the bot’s name and avatar, a webhook message can have any username and avatar you choose. This makes webhooks ideal for displaying formatted notifications, game stats, or logs that look like they come from an external service.
When you send a webhook from a bot command, the flow works like this:
- A user runs a slash command on your Discord server.
- Your bot receives an InteractionCreate event.
- The bot creates or fetches a webhook for the target channel.
- The bot sends the message via the webhook, using a custom name and avatar.
- The bot replies to the user with a confirmation or ephemeral message.
The main benefit is visual consistency. For example, a server management bot can send a webhook message that looks like a system notification rather than a bot reply. The webhook message also cannot be deleted by users without the Manage Webhooks permission, which adds a layer of control.
Before you start, you need the following:
- A Discord bot application created in the Discord Developer Portal.
- The bot added to your server with the Send Messages and Manage Webhooks permissions.
- Node.js installed on your development machine.
- The discord.js library version 14 or later installed in your project.
Steps to Send a Discord Webhook From an Application Command
The implementation uses discord.js and the WebhookClient class. Follow these steps to build the feature.
- Set Up Your Project and Install Dependencies
Create a new folder and initialize a Node.js project. Runnpm init -ythen install discord.js withnpm install discord.js. Create a file namedindex.js. - Create the Bot Client and Register a Slash Command
Inindex.js, import discord.js and create a client with theGatewayIntentBits.Guildsintent. Use theclient.on('ready')event to register a global slash command namedwebhook. The command should accept an optional string option for the message content. - Listen for the InteractionCreate Event
Add an event listener forinteractionCreate. Check if the interaction is a chat input command and if the command name matcheswebhook. - Fetch or Create a Webhook for the Channel
Inside the command handler, get the channel where the command was run usinginteraction.channel. Usechannel.fetchWebhooks()to see if a webhook already exists. If not, create one withchannel.createWebhook(). Store the webhook ID and token for reuse. - Create a WebhookClient and Send the Message
Use the webhook ID and token to instantiate aWebhookClient. CallwebhookClient.send({ content: 'Your message here', username: 'Custom Name', avatarURL: 'https://example.com/avatar.png' }). This sends the webhook message to the channel. - Reply to the User
After the webhook message is sent, reply to the interaction withinteraction.reply({ content: 'Webhook sent!', ephemeral: true }). This confirms the action without cluttering the channel.
Here is a complete code example for the command handler:
const { Client, GatewayIntentBits, WebhookClient } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once('ready', async () => {
console.log(`Logged in as ${client.user.tag}`);
const command = {
name: 'webhook',
description: 'Send a webhook message',
options: [
{
name: 'message',
description: 'The message content',
type: 3, // STRING
required: true,
},
],
};
await client.application.commands.create(command);
});
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName !== 'webhook') return;
const channel = interaction.channel;
let webhooks = await channel.fetchWebhooks();
let webhook = webhooks.find(w => w.owner.id === client.user.id);
if (!webhook) {
webhook = await channel.createWebhook({
name: 'Bot Webhook',
});
}
const webhookClient = new WebhookClient({ id: webhook.id, token: webhook.token });
const messageContent = interaction.options.getString('message');
await webhookClient.send({
content: messageContent,
username: 'Custom Name',
avatarURL: 'https://example.com/avatar.png',
});
await interaction.reply({ content: 'Webhook sent!', ephemeral: true });
});
client.login('YOUR_BOT_TOKEN');
Replace YOUR_BOT_TOKEN with your bot’s token from the Discord Developer Portal. The webhook username and avatar URL are hardcoded in this example. You can make them dynamic by adding command options.
Common Issues When Sending Webhooks From Bot Commands
Webhook Not Created Because of Missing Permissions
If the bot does not have the Manage Webhooks permission, channel.createWebhook() throws an error. Ensure the bot role has this permission enabled in the server settings. Also check that the bot has the Send Messages permission for the target channel.
Webhook Token Expires After Bot Restart
A webhook token is generated when the webhook is created. If you delete the webhook and recreate it, the token changes. Store the webhook ID and token in a database or environment variable to avoid losing access. The token does not expire unless the webhook is deleted.
Webhook Message Does Not Appear in the Channel
If the webhook message does not appear, check that the channel ID is correct. The interaction.channel property returns the channel where the command was run. If you want to send the webhook to a different channel, fetch that channel by ID using client.channels.fetch().
Rate Limits When Sending Multiple Webhooks
Discord applies rate limits to webhook messages. The limit is 30 messages per webhook per 60 seconds. If your bot sends many webhooks quickly, implement a queue or delay to avoid hitting the rate limit. Use a library like p-limit to control concurrency.
| Item | WebhookClient.send() | channel.send() |
|---|---|---|
| Message appearance | Custom username and avatar | Bot username and avatar |
| Permission required | Manage Webhooks and Send Messages | Send Messages only |
| Can be deleted by users | No, only by users with Manage Webhooks | Yes, by any user with Manage Messages |
| Rate limit | 30 per 60 seconds per webhook | 5 per 5 seconds per channel |
| Supports embeds | Yes | Yes |
Now you can make your bot send webhook messages when a user runs a slash command. The webhook appears with a custom name and avatar, giving your command responses a professional look. For advanced use, add command options to let users set the webhook name and avatar dynamically. Consider storing webhook IDs in a database so the bot can reuse them across restarts.