Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/disgoorg/disgo/llms.txt

Use this file to discover all available pages before exploring further.

DisGo provides a dedicated webhook client for sending messages through Discord webhooks without requiring a bot token. This is useful for notifications, logging, and integrations.

Creating a webhook client

You can create a webhook client using either the webhook ID and token, or by parsing a webhook URL.

Using ID and token

import (
    "github.com/disgoorg/disgo/webhook"
    "github.com/disgoorg/snowflake/v2"
)

webhookID := snowflake.MustParse("YOUR_WEBHOOK_ID")
webhookToken := "YOUR_WEBHOOK_TOKEN"

client := webhook.New(webhookID, webhookToken)
defer client.Close(context.TODO())

Using webhook URL

If you have a webhook URL from Discord, you can parse it directly:
webhookURL := "https://discord.com/api/webhooks/123456789/abcdefghijklmnop"

client, err := webhook.NewWithURL(webhookURL)
if err != nil {
    // Handle invalid URL
}
defer client.Close(context.TODO())
Webhook tokens are sensitive credentials. Never expose them in client-side code or public repositories.

Sending messages

The webhook client provides several methods for sending messages.

Basic message

messageCreate := discord.NewWebhookMessageCreateBuilder().
    SetContent("Hello from DisGo!").
    Build()

message, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{Wait: true},
)
if err != nil {
    // Handle error
}
Setting Wait: true returns the created message. Without it, the method returns nil for the message.

Content shorthand

For simple text messages, use the convenience method:
message, err := client.CreateContent("Simple text message")
if err != nil {
    // Handle error
}

Messages with embeds

embed := discord.NewEmbedBuilder().
    SetTitle("Alert").
    SetDescription("Something important happened").
    SetColor(0xFF0000).
    SetTimestamp(time.Now()).
    AddField("Status", "Critical", true).
    AddField("Count", "42", true).
    Build()

messageCreate := discord.NewWebhookMessageCreateBuilder().
    AddEmbeds(embed).
    Build()

message, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{Wait: true},
)

Embeds shorthand

embeds := []discord.Embed{
    discord.NewEmbedBuilder().
        SetTitle("First Embed").
        Build(),
    discord.NewEmbedBuilder().
        SetTitle("Second Embed").
        Build(),
}

message, err := client.CreateEmbeds(embeds)

Username and avatar overrides

Webhooks can override the username and avatar for each message:
messageCreate := discord.NewWebhookMessageCreateBuilder().
    SetContent("Custom identity message").
    SetUsername("Custom Bot Name").
    SetAvatarURL("https://cdn.example.com/avatar.png").
    Build()

message, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{Wait: true},
)
Username changes are rate limited. Don’t change the username with every message or you may be rate limited.

Thread support

Webhooks can post messages to threads within the webhook’s channel.

Post to existing thread

messageCreate := discord.NewWebhookMessageCreateBuilder().
    SetContent("Message in thread").
    Build()

message, err := client.CreateMessageInThread(
    messageCreate,
    threadID,
)

Create thread with message

messageCreate := discord.NewWebhookMessageCreateBuilder().
    SetContent("First message in new thread").
    Build()

message, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{
        Wait:       true,
        ThreadName: "New Thread Name",
    },
)

Editing and deleting messages

Edit a message

content := "Updated content"
messageUpdate := discord.NewWebhookMessageUpdateBuilder().
    SetContent(content).
    Build()

updatedMessage, err := client.UpdateMessage(
    message.ID,
    messageUpdate,
    rest.UpdateWebhookMessageParams{},
)

Update shortcuts

// Update content only
message, err := client.UpdateContent(messageID, "New content")

// Update embeds only
message, err := client.UpdateEmbeds(messageID, []discord.Embed{embed})

Delete a message

err := client.DeleteMessage(messageID)
if err != nil {
    // Handle error
}

Thread message operations

// Update message in thread
message, err := client.UpdateMessageInThread(
    messageID,
    messageUpdate,
    threadID,
)

// Delete message in thread
err := client.DeleteMessageInThread(messageID, threadID)

Managing the webhook

Get webhook information

webhook, err := client.GetWebhook()
if err != nil {
    // Handle error
}

fmt.Printf("Webhook: %s\n", webhook.Name)
fmt.Printf("Channel: %s\n", webhook.ChannelID)

Update webhook

name := "New Webhook Name"
avatar := "https://cdn.example.com/new-avatar.png"

webhookUpdate := discord.WebhookUpdateWithToken{
    Name:   &name,
    Avatar: &avatar,
}

updatedWebhook, err := client.UpdateWebhook(webhookUpdate)
if err != nil {
    // Handle error
}

Delete webhook

err := client.DeleteWebhook()
if err != nil {
    // Handle error
}
Deleting a webhook is permanent and cannot be undone. Make sure you really want to delete it.

Rate limiting and delays

DisGo automatically handles Discord’s rate limits, but you can add additional delays:
message, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{Wait: true},
    rest.WithDelay(2*time.Second), // Wait 2 seconds before sending
)

Concurrent webhook requests

Webhooks are perfect for concurrent operations since they don’t share global rate limits with bot operations:
var wg sync.WaitGroup

for i := 1; i <= 10; i++ {
    wg.Add(1)
    go func(num int) {
        defer wg.Done()

        messageCreate := discord.NewWebhookMessageCreateBuilder().
            SetContentf("Message #%d", num).
            Build()

        _, err := client.CreateMessage(
            messageCreate,
            rest.CreateWebhookMessageParams{Wait: true},
            rest.WithDelay(time.Duration(num)*time.Second),
        )
        if err != nil {
            slog.Error("failed to send message",
                slog.Int("num", num),
                slog.Any("err", err),
            )
        }
    }(i)
}

wg.Wait()

Complete notification example

Here’s a complete example for sending server notifications:
package main

import (
    "context"
    "fmt"
    "log/slog"
    "os"
    "time"

    "github.com/disgoorg/disgo/discord"
    "github.com/disgoorg/disgo/rest"
    "github.com/disgoorg/disgo/webhook"
)

func main() {
    webhookURL := os.Getenv("WEBHOOK_URL")
    
    client, err := webhook.NewWithURL(webhookURL)
    if err != nil {
        slog.Error("invalid webhook URL", slog.Any("err", err))
        return
    }
    defer client.Close(context.TODO())

    // Send startup notification
    sendNotification(client, "Server Started", "Application is now running", 0x00FF00)

    // Simulate some work
    time.Sleep(5 * time.Second)

    // Send status update
    sendNotification(client, "Status Update", "Processing 1000 items", 0x0000FF)

    // Send error notification
    sendNotification(client, "Error Occurred", "Failed to connect to database", 0xFF0000)
}

func sendNotification(client *webhook.Client, title, description string, color int) {
    embed := discord.NewEmbedBuilder().
        SetTitle(title).
        SetDescription(description).
        SetColor(color).
        SetTimestamp(time.Now()).
        SetFooter("Server Monitor", "").
        Build()

    messageCreate := discord.NewWebhookMessageCreateBuilder().
        AddEmbeds(embed).
        Build()

    message, err := client.CreateMessage(
        messageCreate,
        rest.CreateWebhookMessageParams{Wait: true},
    )
    if err != nil {
        slog.Error("failed to send notification", slog.Any("err", err))
        return
    }

    fmt.Printf("Sent notification: %s (ID: %s)\n", title, message.ID)
}

Configuration options

You can customize the webhook client with configuration options:
import (
    "github.com/disgoorg/disgo/rest"
    "github.com/disgoorg/disgo/webhook"
)

client := webhook.New(webhookID, webhookToken,
    webhook.WithLogger(logger),
    webhook.WithRestClient(customRestClient),
    webhook.WithRestClientConfigOpts(
        rest.WithHTTPClient(httpClient),
        rest.WithUserAgent("CustomBot/1.0"),
    ),
)
Available options:
  • WithLogger(*slog.Logger) - Set custom logger
  • WithRestClient(rest.Client) - Use custom REST client
  • WithRestClientConfigOpts(...rest.ClientConfigOpt) - Configure REST client

Best practices

The webhook client maintains a connection pool. Always close it when done:
client := webhook.New(webhookID, webhookToken)
defer client.Close(context.TODO())
Set Wait: true only when you need the returned message object:
// Need message ID for later operations
message, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{Wait: true},
)

// Fire and forget
_, err := client.CreateMessage(
    messageCreate,
    rest.CreateWebhookMessageParams{Wait: false},
)
Even with automatic rate limiting, be mindful of Discord’s limits:
// Bad: Sending too many messages too quickly
for i := 0; i < 1000; i++ {
    client.CreateContent(fmt.Sprintf("Message %d", i))
}

// Good: Add delays between messages
for i := 0; i < 1000; i++ {
    client.CreateMessage(
        messageCreate,
        rest.CreateWebhookMessageParams{Wait: true},
        rest.WithDelay(time.Second),
    )
}
Treat webhook tokens like passwords:
// Good: Load from environment or secret manager
webhookURL := os.Getenv("WEBHOOK_URL")

// Bad: Hardcode in source code
webhookURL := "https://discord.com/api/webhooks/..."

Build docs developers (and LLMs) love