Recently more and more people have been discovering the power of personal Discord servers. You can use them to store files, write notes, mess around with bots, and more.
In this article, I would like to show you how to hook up a contact form on your website to send mail to your Discord server. It's free, it's easy, and it does not even require a backend server.
Before getting started, you should know basic HTML and JavaScript, and you should have a Discord account and a private Discord server (use the plus button in the bottom left of the Discord desktop client to create one).
Creating a Webhook
First, we need to create a webhook in Discord. Decide which channel in your private server you want to receive mail in, and click the settings button. I'm going to use the #general
channel:
In the settings window, go to the Integrations
section, and click Create Webhook
:
After the webhook has been created, give it a name (I chose Contacts
), and click Copy Webhook URL
. This will copy the webhook URL to your clipboard. We'll need it in a little bit.
Making the Contact Form
This article is going to focus on how to call the webhook via JavaScript, so I'm going to gloss over the HTML part a bit. If you want to follow along, you can copy and paste this code into a file called contact.html
:
<html>
<head>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body class="container mt-5">
<form onsubmit="sendContact(event)">
<div class="mb-3">
<label for="emailInput" class="form-label">Enter your email address</label>
<input type="email" class="form-control" id="emailInput">
</div>
<div class="mb-3">
<label for="messageInput" class="form-label">Enter your message</label>
<textarea class="form-control" id="messageInput" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<script>
async function sendContact(ev) {
}
</script>
</body>
</html>
It's just really basic HTML boilerplate plus bootstrap to make things look slightly non-gross.
If you open the contact.html
file in your browser, this is what you will see:
And if you click the Submit
button, it will call the sendContact
function, which does nothing!
So let's make it do something. Let's start writing code in the sendContact()
function.
First, no surprises, let's prevent the default form submit action, and let's get the email address and message that the user input:
ev.preventDefault();
const senderEmail = document
.getElementById('emailInput').value;
const senderMessage = document
.getElementById('messageInput').value;
Next let's craft the body that we're going to send to the webhook. The body should be a Discord message object, which is clearly documented in the Discord API documentation.
In our case, we just want a message with a title and two sub-sections: Sender
and Message
. That's going to look like this:
const webhookBody = {
embeds: [{
title: 'Contact Form Submitted',
fields: [
{ name: 'Sender', value: senderEmail },
{ name: 'Message', value: senderMessage }
]
}],
};
Now we just use fetch
to send the webhook. Remember that webhook URL you copied earlier? You'll need it here. Paste it in as the value of the webhookUrl
variable:
const webhookUrl = 'YOUR URL HERE';
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(webhookBody),
});
Then let's show an alert and tell the user whether the request was successful:
if (response.ok) {
alert('I have received your message!');
} else {
alert('There was an error! Try again later!');
}
That's it! Refresh the page, type in an email and message, and click Submit.
If you did everything right, you should hear a satisfying little ting sound from your Discord client telling you that there has been a new message in your server. Check it out:
With just a little bit of frontend code we now officially have our contact form sending mail to our private Discord server.
Full Code
Here's the full code that I used for this demo. Remember to replace YOUR URL HERE
with your webhook URL.
<html>
<head>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body class="container mt-5">
<form onsubmit="sendContact(event)">
<div class="mb-3">
<label for="emailInput" class="form-label">Enter your email address</label>
<input type="email" class="form-control" id="emailInput">
</div>
<div class="mb-3">
<label for="messageInput" class="form-label">Enter your message</label>
<textarea class="form-control" id="messageInput" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<script>
async function sendContact(ev) {
ev.preventDefault();
const senderEmail = document
.getElementById('emailInput').value;
const senderMessage = document
.getElementById('messageInput').value;
const webhookBody = {
embeds: [{
title: 'Contact Form Submitted',
fields: [
{ name: 'Sender', value: senderEmail },
{ name: 'Message', value: senderMessage }
]
}],
};
const webhookUrl = 'YOUR URL HERE';
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(webhookBody),
});
if (response.ok) {
alert('I have received your message!');
} else {
alert('There was an error! Try again later!');
}
}
</script>
</body>
</html>
A Word of Caution
It's important to understand that putting your webhook link into your frontend code means that a malicious actor could take it and use it in a script to spam you or even send you nasty images.
Fortunately, that's about the worst they could do. The link only allows sending messages in your server, and does not allow reading messages, taking any sort of administrative action, or really anything else.
So while embedding the webhook link into your small, personal website is likely going to be fine, I would not do this for anything major, or if I had a lot of tech-savvy enemies. I also would not do this in a Discord channel that many other people have read access to.
If you are concerned about these risks but you still want to use a Discord webhook for your contact form, you would need some sort of backend to be a middleman for the requests. In fact, I use that approach for my own personal site.
Blocked TLDs
It has come to my attention that this method does not work for *.tk
domains. Discord seems to selectively not return the needed CORS headers for TLDs that it does not like. Besides getting a new domain, your only option involves proxying the request through a backend server.
Conclusion
Getting contact forms to work well (for free) can actually be harder than it sounds. I used to use Gmail's SMTP server via my personal website's backend for this, but it would frequently stop working for "security reasons" until I went into my account and reminded Google that it's legit traffic. I ended up swapping in a Discord webhook instead and haven't looked back. It's super convenient and easy to set up, and has worked very reliably.
Top comments (8)
thxxxx, u r god
When I tried using this I kept getting a cors error not allowing me to post the webhook message in my server. I talked to my friend and he said i'd have to do it with traditional code not next.js, and I'd have to do something like:
const { WebhookClient } = require("discord.js");
const hook = WebhookClient({id: "ID", token: "TOKEN" });
hook.send({});
but idk what im doing lol. Do you know how to get around the cors error without having to rewrite all the code?
You're having CORS errors from the POST request to the Discord webhook? I wouldn't expect that but it's possible something has changed on Discord's side. In this case an opaque response should be fine, so if you just add
mode: 'no-cors'
to thefetch
request, that may resolve your issue.Oh okay, cool. How exactly do I do that?
NVM on that lol, i got it but now im getting a 403 error. Which made me think, if i am not allowed to use the link why not just call the webhook id and token using discord.js xD not sure if it'll work but imma try lol. Thanks for your help! And the article 😂
Wasn't working first because of the alert popup, which was blocked by the browser.
At least I figured that one out : ) One quick question:
how can I send just plain text, no embed?
Hi, to send plain text, simply send a
content
property in the webhook body. If you're using the example code, just replace thewebhookBody
declaration with this:thanks, works perfectly.
For other people, I stripped it down to just a text box that sends the plain text to the webhook.