Guide to Discord Webhooks Features and Best Practices
In our Discord webhook series, we have taken a look at the first steps required to get started with Discord webhooks. We also got some hands-on experience by setting up Discord webhooks using the Discord web interface and the Discord webhooks API.
Now that we have all the Discord webhooks basics covered, we will take a look at best practices that help us deploy reliable, secure, and scalable Discord webhooks in production environments.
If you're still new to Discord webhooks, I advise that you check out the beginner articles linked above before you proceed with this one. However, if you understand how Discord webhooks work and have already published at least one, let's get started.
Discord webhook features
Before we get into the best practices, let's get some familiarity with the features of Discord webhooks. This will help you understand some of the considerations we will take into account when discussing the best practices later on.
Below is a table that displays Discord webhook features and how they each relate to one or more best practices.
Feature | Notes | Related Best Practice |
---|---|---|
Webhook configuration | Admin interface or webhook API | Integration development |
Webhook URL | Tied to one channel, can be used by multiple message sources | Integration development |
Request method | POST | Integration development, troubleshooting, and testing |
Expected message format | JSON | Integration development, testing and troubleshooting |
Supported webhook payload elements | Text, images, attachments | Integration development |
Retry logic | None - depends on the message source (e.g. GitHub, Slack) | Failure recovery |
Browsable log | None | Troubleshooting and testing, failure recovery |
Alerting | No alerts are sent upon webhook failure | Failure recovery, troubleshooting and testing |
Now that we have a solid understanding of the Discord webhook features, we can go ahead and discuss best practices for using Discord webhooks in our production infrastructure.
Recommended best practices for using Discord webhooks
Local troubleshooting and testing
Unlike GitHub and Slack, which have documented webhook URL formats that allow Discord to recognize and parse their payloads, Discord webhook payloads must be sent in a specific format. If this message format is not adhered to, Discord will reject the webhook and your message will not get delivered to your Discord channel.
The best way to ensure that you're always sending the right type of message to Discord is to properly test your communication setup from source to Discord.
Imagine a situation where you need to communicate the build status of your deployment pipeline on a CI/CD server, like CircleCI, to Discord. So, like this guy, you write a script within your build pipeline to communicate the success or failure of the build to Discord using a Discord webhook. These build status messages are to be sent to your team's Discord channel where an action will be taken if a failure message is received. If bugs are present in the script that sends the status of the build and it sends the wrong message format to Discord, Discord will reject the webhook.
This means that your build status notification will not be received in the channel. In the case of a failed build, such an error will prevent the team from receiving the failure notification and the situation will not be addressed. This is not a situation you want to put your team in.
This type of unwanted scenario is exactly why you should properly test and troubleshoot the code you write to send messages to a Discord channel through a Discord webhook.
Here are some of the types of tests you should consider for your webhook setup:
- Check that you're using the correct webhook URL (you will be surprised how easily you can get this wrong or mix it up)
- Check that each payload property's value is being sent in the right format (e.g. no strings where an integer is required)
- Check that you're using the proper request method (
POST
) and message format (JSON) - Ensure that the
content
property, which carries the textual part of the Discord message, is properly escaping quotes and that special characters are properly encoded to their string equivalents - Ensure that you're getting a
201
response code after sending a message to confirm delivery
The above list is not exhaustive; however, it should serve as a guide for the type of checks you should be performing when testing your Discord webhook setup.
Asynchronous processing to ensure reliability
For high-volume users, i.e. your Discord webhook endpoint is going to be receiving a lot of requests, and most of them concurrently, synchronous processing (the default) is not the best strategy for you.
The Discord API is rate-limited to ensure that the service is not overloaded with requests. Webhook URL endpoints are part of the Discord API and have at least the Discord API global rate limit applied. Once this limit is exceeded by your requests, Discord will stop processing your webhooks or ban your client.
I was speaking to a CircleCI staff member recently and he revealed to me that some companies process tens of thousands of builds concurrently. Going by the previous example of receiving build status messages in a Discord channel, such a high number of concurrent builds has the potential to hit Discord rate limits. This is where asynchronous processing comes in.
With asynchronous processing, you can ingest all the status messages from CircleCI and relay them to Discord at a rate that will not exceed its API limits. This helps your Discord webhook messaging setup so that it is elastic enough to withstand high volumes of requests.
One of the ways to introduce asynchronous processing into your Discord webhook setup is to use a message queue. Message queue frameworks like Kafka and RabbitMQ can be used to build a custom asynchronous processing system into your Discord webhooks by placing them between your message source (CircleCI in this example) and Discord. However, as efficient as this solution is, it is not an easy component to build and adds more complexity to your system.
If you want to build and scale fast, consider using Hookdeck's webhook ingestion service. With Hookdeck, you can introduce asynchronous processing into your Discord webhooks setup in a matter of minutes.
Logging for visibility and failure troubleshooting
Failure is inevitable when it comes to webhooks. No matter the amount of testing you have done, your webhooks can still fail due to factors mostly beyond your control.
Still using the build status example, let's assume your Discord webhook is working fine and you're successfully receiving your build messages in your Discord channel. Then suddenly, you resume work on a certain day only to realize that you're no longer receiving messages, even as more builds are being processed. You assume that the issue is not from your code because you were receiving the messages up until today. However, this is just an assumption because you have no visibility into the webhook request process and Discord has no browsable log for webhooks. This type of blind spot is not healthy for a system that strives to be reliable.
To recover from failure, you need the ability to trace what the cause of the failure is. This can be achieved by logging the webhook request process. Tools like the ELK stack can be used to log and provide visibility into your webhook communication process. Zipkin is also a tool that can be used for HTTP request tracing, and provides visibility into the webhook request process.
Just like message queues, these logging frameworks require a good amount of expertise and time to set up. This is why Hookdeck comes bundled with logging capabilities. Hookdeck also gives you intuitive dashboards with information to troubleshoot any issues that arise with your Discord webhooks.
Failure recovery using a retry system
So what happens after you have looked through your logs to detect why your webhooks failed and the situation is now fixed? The webhooks that failed need to be re-forwarded to Discord to ensure that you're capturing all the necessary information in your channel.
It is acceptable to have webhooks fail, but what is not acceptable is the inability to recover from failure. Thus, you need a system to automatically or manually retry all failed webhooks once the issue that caused them to fail has been fixed.
You may be asking, "what is so bad about missing a couple of messages?" Well, with our build status messaging example, this can be a major problem if some of those missed messages reflect failed builds. If you decide to skip them, they will not reach the team members who are supposed to act on them.
An automatic retry system will enable the Discord webhook messaging system to heal itself from disasters. A manual retry system will give you more control over which webhooks to retry and when to retry them.
A retry mechanism can be built by properly ingesting your webhooks, logging them to monitor their status, and writing application logic to resend failed webhooks.
A retry mechanism is another feature you get when using Hookdeck to process your webhooks. You can define when and how your failed webhooks should be retried automatically, and also manually trigger a webhook retry.
Secure your Discord webhook URL
One big problem Discord users have is message spamming, and having an exposed webhook URL doesn't help matters. Discord webhook URLs have no authentication strategy, so if your webhook URL falls into the hands of an ill-intentioned individual, they can spam your channel with unwanted messages.
Thus, it is important that you only share your webhook URL with authorized members of your team and develop policies around protecting it from unwanted exposure. You can also make sure that members of your Discord channel who don’t work with webhooks do not have the MANAGE_WEBHOOKS
permission.
If security is a very important part of your operations, I advise that you send your webhook requests to Discord through a proxy server. This proxy server will be the only entity that knows your Discord webhook URL. You can store the webhook URL as an environment variable on your proxy server. The proxy server also allows you to build security checks into how your webhook requests are made. Examples of such security controls are:
- Creating an "Allow" list of IP addresses that can communicate with the proxy server
- Integrating token authentication to ensure that all client requests are verified
- Denying unwanted content by filtering the content of each webhook request
This way, only approved messages will be sent to your Discord channel.
Conclusion
Discord webhooks are very simple in design and work efficiently. The simplicity of this design often sacrifices a lot of standard software architecture characteristics like scalability, reliability, and security. The best practices we have discussed in this article allow you to build resilience into your Discord webhooks and ensure that you're getting optimal performance.
Hookdeck takes away many of the worries that come with webhooks in production environments by helping you ingest and asynchronously process your webhooks, providing visibility with clear visualizations and helping you configure retry mechanisms that suit your needs. You can start with a free Hookdeck account today.