Modern, TypeScript-first Discord Webhook client with full type safety, ESM support, and modern features.
- TypeScript-first: Full type safety with comprehensive type definitions
- Modern: ESM and CJS dual build support
- Safe defaults: Prevents accidental
@everyonementions - Rate limit handling: Automatic retry with configurable backoff
- Lightweight: Minimal dependencies (only
undici) - Simple API: Easy to use and predictable
- EmbedBuilder: Fluent API for building embeds with method chaining
- AlertClient: Pre-configured alerts for error monitoring (info/success/warn/error)
npm install discord-webhook-kitimport { WebhookClient } from "discord-webhook-kit";
const webhook = new WebhookClient(
"https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"
);
// Send a simple message
await webhook.send("Hello, Discord!");
// Send with custom username and avatar
await webhook.send({
content: "Hello from custom bot!",
username: "My Bot",
avatar_url: "https://example.com/avatar.png",
});const webhook = new WebhookClient(WEBHOOK_URL);
await webhook.send("Hello, World!");await webhook.send({
content: "Check out this embed!",
embeds: [
{
title: "Embed Title",
description: "This is the embed description",
color: 0x00ff00, // Green color
fields: [
{ name: "Field 1", value: "Value 1", inline: true },
{ name: "Field 2", value: "Value 2", inline: true },
],
footer: {
text: "Footer text",
},
timestamp: new Date().toISOString(),
},
],
});import { WebhookClient, EmbedBuilder } from "discord-webhook-kit";
const embed = new EmbedBuilder()
.setTitle("My title here")
.setAuthor(
"Author here",
"https://cdn.discordapp.com/embed/avatars/0.png",
"https://www.google.com"
)
.setURL("https://www.google.com")
.addField("First field", "this is inline", true)
.addField("Second field", "this is not inline")
.setColor("#00b0f4")
.setThumbnail("https://cdn.discordapp.com/embed/avatars/0.png")
.setDescription("Oh look a description :)")
.setImage("https://cdn.discordapp.com/embed/avatars/0.png")
.setFooter(
"Hey its a footer",
"https://cdn.discordapp.com/embed/avatars/0.png"
)
.setTimestamp();
await webhook.send({ embeds: [embed] });const webhook = new WebhookClient(WEBHOOK_URL, {
username: "Notification Bot",
avatarUrl: "https://example.com/bot-avatar.png",
retryOnRateLimit: true,
maxRetries: 5,
timeout: 10000,
});
// All messages will use the default username and avatar
await webhook.send("This uses default settings!");const webhook = new WebhookClient(WEBHOOK_URL, {
threadId: "1234567890",
});
await webhook.send("Message sent to a specific thread!");const webhook = new WebhookClient(WEBHOOK_URL, {
retryOnRateLimit: false,
});
try {
await webhook.send("Message");
} catch (error) {
if (error instanceof DiscordWebhookError && error.isRateLimit()) {
console.log("Rate limited! Try again later.");
}
}new WebhookClient(webhookUrl: string, options?: WebhookClientOptions)| Option | Type | Default | Description |
|---|---|---|---|
username |
string |
- | Default username for all messages |
avatarUrl |
string |
- | Default avatar URL for all messages |
wait |
boolean |
true |
Wait for server confirmation |
threadId |
string |
- | Thread ID to send messages to |
retryOnRateLimit |
boolean |
true |
Automatically retry on rate limit |
maxRetries |
number |
3 |
Maximum retry attempts on rate limit |
timeout |
number |
30000 |
Request timeout in milliseconds |
Sends a message to the Discord webhook.
// String shorthand
await webhook.send("Hello!");
// Full message object
await webhook.send({
content: "Hello!",
username: "Custom Name",
embeds: [{ title: "Embed" }],
});Returns a masked version of the webhook URL safe for logging.
console.log(webhook.getMaskedUrl());
// Output: /webhooks/1234567890/abcd...wxyz| Field | Type | Description |
|---|---|---|
content |
string |
Message content (max 2000 chars) |
username |
string |
Override webhook username |
avatar_url |
string |
Override webhook avatar |
tts |
boolean |
Text-to-speech message |
embeds |
WebhookEmbed[] |
Array of embeds (max 10) |
allowed_mentions |
AllowedMentions |
Mention control |
flags |
number |
Message flags |
thread_name |
string |
Thread name (for forum channels) |
applied_tags |
string[] |
Tag IDs (for forum channels) |
| Field | Type | Description |
|---|---|---|
title |
string |
Embed title (max 256 chars) |
description |
string |
Embed description (max 4096) |
url |
string |
URL for title link |
color |
number |
Color code (decimal) |
timestamp |
string |
ISO8601 timestamp |
footer |
WebhookEmbedFooter |
Footer object |
image |
WebhookEmbedMedia |
Image object |
thumbnail |
WebhookEmbedMedia |
Thumbnail object |
author |
WebhookEmbedAuthor |
Author object |
fields |
WebhookEmbedField[] |
Fields array (max 25) |
Builder class for creating embeds with method chaining.
| Method | Description |
|---|---|
setTitle(title) |
Set embed title |
setDescription(description) |
Set embed description |
setURL(url) |
Set embed URL |
setColor(color) |
Set color (hex string or number) |
setAuthor(name, iconUrl?, url?) |
Set author info |
setThumbnail(url) |
Set thumbnail image |
setImage(url) |
Set main image |
setFooter(text, iconUrl?) |
Set footer |
setTimestamp(timestamp?) |
Set timestamp (default: now) |
addField(name, value, inline?) |
Add a field |
addFields(...fields) |
Add multiple fields |
toJSON() |
Convert to plain embed object |
Custom error class for Discord API errors.
try {
await webhook.send("Message");
} catch (error) {
if (error instanceof DiscordWebhookError) {
console.log("Status:", error.status);
console.log("Message:", error.message);
console.log("Raw body:", error.rawBody);
console.log("Request ID:", error.requestId);
console.log("Is rate limit:", error.isRateLimit());
}
}AlertClient provides a convenient way to send formatted alerts to Discord, perfect for error monitoring and application notifications.
import { AlertClient } from "discord-webhook-kit";
const alert = new AlertClient(WEBHOOK_URL, {
appName: "My API",
environment: "production",
username: "Alert Bot",
avatarUrl: "https://example.com/avatar.png",
});
// Info alert (blue)
await alert.info("User logged in", {
fields: [{ name: "User ID", value: "12345" }],
});
// Success alert (green)
await alert.success("Payment completed", {
fields: [
{ name: "Amount", value: "$100.00" },
{ name: "Order ID", value: "ORD-123" },
],
});
// Warning alert (yellow)
await alert.warn("High memory usage detected", {
fields: [{ name: "Usage", value: "85%" }],
});
// Error alert (red) - with Error object auto-formatting
try {
await riskyOperation();
} catch (err) {
await alert.error(err); // Auto-formats Error with stack trace
}
// Error alert with custom message and error
await alert.error("Database operation failed", {
error: caughtError,
fields: [{ name: "Query", value: "SELECT * FROM users" }],
});| Option | Type | Default | Description |
|---|---|---|---|
appName |
string |
"Alert" |
Application name shown in title |
environment |
string |
- | Environment (e.g., "production") |
username |
string |
- | Default webhook username |
avatarUrl |
string |
- | Default webhook avatar |
| Level | Color | Hex Code |
|---|---|---|
info |
Blue | #3498db |
success |
Green | #00ff1d |
warn |
Yellow | #f1c40f |
error |
Red | #ff0000 |
When passing an Error object to alert.error(), it automatically:
- Extracts error name and message
- Includes
codeandstatusproperties if present - Formats stack trace in a code block
- Respects Discord's field limits (auto-trims long content)
By default, discord-webhook-kit disables @everyone and @here mentions to prevent accidental mass pings:
// Default: allowed_mentions is set to { parse: [] }
await webhook.send("@everyone Hello!"); // Won't ping everyone
// To enable mentions, explicitly set allowed_mentions
await webhook.send({
content: "@everyone Hello!",
allowed_mentions: { parse: ["everyone"] },
});- Node.js 18.0.0 or higher
- Discord.js Documentation - Official Discord.js docs
- Discord Webhook API - Official Discord API documentation
MIT