Russian translation by DarkPro1337#4304
If you do not know anything about JSON, please, spend some time on learning JSON structure.
What is JSON?
JSON stands for
N.B. Don't be scared. JSON is easy to learn and use! This is not a programming language!!!
JSON is data-storing format that easy to read and write for humans and robots.
key: value
- this is key-value pair.:
), no exceptions.,
), no exceptions.key
is always text inserted between double quotes (" "
).value
can be different types:"sample text"
, "cool\nthings"
42
, -300
, 6.62e-34
{ "name": "Jason", "likes": ["apples", "oranges"] }
["apple", "banana", "orange"]
, [1, true, 3, "meow"]
true
, false
null
Strings can store any characters you want, but some of them need to be escaped:
\"
\\
\/
(escaping is optional)\n
(use this if you want to add newline to value)\r
\t
\b
\f
\uxxxx
{
"name": "Pumpkin",
"age": 7,
"likes": [
"patting",
"sleeping in a garden",
"salmon"
],
"appearance": "Orange Tabby",
"owner_name": "Jane Doe",
"phone_number": "+447712345678",
"address": {
"country": "England",
"city": "London",
"street": "Crown Street",
"house": 38,
"notes": null
}
}
Before using Webhooks you have to know the structure. All elements listed here are optional but request body should contain content
, embeds
or attachments, otherwise request will fail.
username
- overrides the predefined username of the webhookavatar_url
- overrides the predefined avatar of the webhookcontent
- text message, can contain up to 2000 charactersembeds
- array of embed objects. In comparison with bots, webhooks can have more than one custom embedcolor
- color code of the embed. You have to use Decimal numeral system, not Hexadecimal. You can use SpyColor for that. It has decimal number converter.author
- embed author objectname
- name of authorurl
- url of author. If name
was used, it becomes a hyperlinkicon_url
- url of author icontitle
- title of embedurl
- url of embed. If title
was used, it becomes hyperlinkdescription
- description textfields
- array of embed field objectsname
- name of the fieldvalue
- value of the fieldinline
- if true, fields will be displayed in the same line, 3 per line, 4th+ will be moved to the next linethumbnail
- embed thumbnail objecturl
- url of thumbnailimage
- embed image objecturl
- image urlfooter
- embed footer objecttext
- footer text, doesn't support Markdownicon_url
- url of footer icontimestamp
- ISO8601 timestamp (yyyy-mm-ddThh:mm:ss.msZ
)tts
- makes message to be spoken as with /tts
commandallowed_mentions
- object allowing to control who will be mentioned by messageparse
- array, can include next values: "roles"
, "users"
and "everyone"
, depends on which decides which mentions work. If empty, none mention work.roles
- array, lists ids of roles which can be mentioned with message, remove "roles"
from parse
when you use this one.users
- array, lists ids of roles which can be mentioned with message, remove "users"
from parse
when you use this one.{
"username": "Webhook",
"avatar_url": "https://i.imgur.com/4M34hi2.png",
"content": "Text message. Up to 2000 characters.",
"embeds": [
{
"author": {
"name": "Birdie♫",
"url": "https://www.reddit.com/r/cats/",
"icon_url": "https://i.imgur.com/R66g1Pe.jpg"
},
"title": "Title",
"url": "https://google.com/",
"description": "Text message. You can use Markdown here. *Italic* **bold** __underline__ ~~strikeout~~ [hyperlink](https://google.com) `code`",
"color": 15258703,
"fields": [
{
"name": "Text",
"value": "More text",
"inline": true
},
{
"name": "Even more text",
"value": "Yup",
"inline": true
},
{
"name": "Use `\"inline\": true` parameter, if you want to display fields in the same line.",
"value": "okay..."
},
{
"name": "Thanks!",
"value": "You're welcome :wink:"
}
],
"thumbnail": {
"url": "https://upload.wikimedia.org/wikipedia/commons/3/38/4-Nature-Wallpapers-2014-1_ukaavUI.jpg"
},
"image": {
"url": "https://upload.wikimedia.org/wikipedia/commons/5/5a/A_picture_from_China_every_day_108.jpg"
},
"footer": {
"text": "Woah! So cool! :smirk:",
"icon_url": "https://i.imgur.com/fKL31aD.jpg"
}
}
]
}
Overrides webhook's username. Useful, if you're using the same Webhook URL for several things.
Example:
{
"username": "Cat",
"content": "Hello!"
}
Overrides webhook's avatar. Useful, if you're using the same Webhook URL for several things.
Example:
{
"avatar_url": "https://i.imgur.com/oBPXx0D.png",
"content": "Woof-woof!"
}
Sets content for message sent by webhook.
Example:
{
"content": "*reads manual*"
}
Sets custom embeds for message sent by webhook. embeds
is an array of embeds and can contain up to 10 embeds in the same message.
Examples:
{
"embeds": [{
"title": "Hello!",
"description": "Hi! :grinning:"
}]
}
{
"embeds": [
{
"title": "Meow!",
"color": 1127128
},
{
"title": "Meow-meow!",
"color": 14177041
}
]
}
Adding embeds overrides url embeds.
Sets color for webhook's embed. It equals 0 (transparent) by default. Color requires number instead hex code, so you have to convert hexadecimal color code to decimal number. Color can be defined as number 65280
and as string "65280"
.
I recommend to use SpyColor for color picking, it provides decimal value.
Example:
{
"embeds": [
{
"title": "Meow!",
"color": 1127128
},
{
"title": "Meow-meow!",
"color": "14177041"
}
]
}
Adds Author block to embed. author
is an object which includes three values:
name
- sets name.url
- sets link. Requires name
value. If used, transforms name
into hyperlink.icon_url
- sets avatar. Requires name
value.Example:
{
"embeds": [{
"author": {
"name": "Delivery Girl",
"url": "https://www.reddit.com/r/Pizza/",
"icon_url": "https://i.imgur.com/V8ZjaMa.jpg"
},
"description": "Your pizza is ready!\n:timer:ETA: 10 minutes."
}]
}
Sets title for webhook's embed.
Example:
{
"embeds": [{
"title": "Meow!"
}]
}
Sets link for title in your webhook message. Requires title
variable and turns it into hyperlink.
Example:
{
"embeds": [{
"title": "Google it!",
"url": "https://google.com/"
}]
}
Sets description for webhook's embed.
Example:
{
"embeds": [{
"description": "*Hi!* **Wow!** I can __use__ hyperlinks [here](https://discord.com)."
}]
}
Allows you to use multiple title + description
blocks in embed. fields
is an array of field
objects. Each object includes three values:
name
- sets name for field object. Required;value
- sets description for field object. Required;inline
- if true
then sets field objects in same line, but if you have more than 3 objects with enabled inline
or just too long you will get rows with 3 fields in each one or with 2 fields if you used thumbnail
object. false
by default. Optional.P.S. You can use up to 25 fields in same embed. name
and value
support Discord Markdown.
Example:
{
"embeds": [{
"fields": [
{
"name": "Cat",
"value": "Hi! :wave:",
"inline": true
},
{
"name": "Dog",
"value": "hello!",
"inline": true
},
{
"name": "Cat",
"value": "wanna play? join to voice channel!"
},
{
"name": "Dog",
"value": "yay"
}
]
}]
}
Allows you to add image to the embed. Currently, there is no way to set width/height of the image. url
value must be valid url that starts with http(s)://
or attachment://
, more info about the latter one can be found on the file page.
Example:
{
"embeds": [{
"image": {
"url": "https://i.imgur.com/ZGPxFN2.jpg"
}
}]
}
Allows you to add thumbnail to the embed. Currently, there is no way to set width/height of the thumbnail. url
value must be valid url that starts with http(s)://
or attachment://
, more info about the latter one can be found on the file page.
Example:
{
"embeds": [{
"thumbnail": {
"url": "https://upload.wikimedia.org/wikipedia/commons/3/38/4-Nature-Wallpapers-2014-1_ukaavUI.jpg"
}
}]
}
Allows you to add footer to embed. footer
is an object which includes two values:
text
- sets name for author object. Markdown is disabled here!!!icon_url
- sets icon for author object. Requires text
value.Example:
{
"embeds": [{
"footer": {
"text": "Woah! *So cool!* :smirk:",
"icon_url": "https://i.imgur.com/fKL31aD.jpg"
},
"description": "Your pizza is ready!\n:timer:ETA: 10 minutes."
}]
}
Allows you to add timestamp
to embed. Time stores as String in the next format: "YYYY-MM-DDTHH:MM:SS.MSSZ"
. If footer
was used they will be separated with a bullet (•). Also, this is special field, because it can show different time based on user's device.
P.S. Timestamp is not just text. This is formatted UTC time and date. It will show different time because timezones. Look on example below: I set 12pm but it shows 2pm because UTC+2 timezone.
Example:
{
"embeds": [{
"description": "Time travel!",
"timestamp": "2015-12-31T12:00:00.000Z"
}]
}
Enables text-to-speech for current message, so everyone who have tts enabled with hear it.
Example:
{
"content": "Hi",
"tts": true
}
Allows to suppress pings by users, roles or everyone/here mentions. allowed_mentions
object and can contain next parameters:
parse
- array and can include next values:users
- array with id of users, allows to limit which users may be pinged.roles
- array with id of roles, allows to limit which roles may be pinged.Don't include users
and roles
both in allowed_mentions
and parse
, , if you want
Here's some examples:
nobody will be pinged by this message.
{
"content": "@everyone <@&role-id> <@user-id>",
"allowed_mentions": { "parse": [] }
}
only users that didn't suppress everyone/here mentions will be pinged.
{
"content": "@everyone <@&role-id> <@user-id>",
"allowed_mentions": { "parse": ["everyone"] }
}
only user with user-id
will be pinged, in this case @everyone
won't ping anyone. user2
mention is not in content so no ping for them.
{
"content": "@everyone <@&role-id> <@user-id>",
"allowed_mentions": { "users": ["user-id", "user2-id"] }
}
similar for roles.
{
"content": "@everyone <@&role-id> <@user-id>",
"allowed_mentions": { "roles": ["role-id"] }
}
Will ping everyone with disabled everyone/here mention suppress, all users mentioned in message (i. e. user-id
user) or with role-id
role (unless enabled role mention suppress).
{
"content": "@everyone <@&role-id> <@user-id>",
"allowed_mentions": {
"parse": ["everyone", "users"],
"roles": ["role-id"]
}
}
Sending files using webhooks is only possible using Content-Type: multipart/form-data
header. Using this method also means you have to set json body as value of payload_json
parameter.
Parameter names should have unique names otherwise they will collide and only first file from ones with identical names will be shown.
Example:
file1=@cat.jpg
file2=@dog.jpg
payload_json={"embeds":[{"title":"test"}]}
To put image attachment inside embed use attachment://
with its filename (not field name). That will also hide attachment from the message.
file1=@pizza.jpg
payload_json={"embeds":[{"image":{"url":"attachment://pizza.jpg"}}]}
IFTTT is a very awesome service for connecting services. It supports Webhooks, so we can use it with Discord.
Visit IFTTT and create yourself an account (if you don't have one yet).
⚠️ Attention! Keep URL safe, don't share them with people you don't trust, don't post it in public channels, don't give your server mods Administrator or Manage Webhooks permission as it gives access to them as well. Webhooks are quite powerful and can cause some troubles, which including, but not limited to, @everyone/@here mentions (webhooks ignore channel permissions), message/image spam (webhooks have max 5 requests in 2 seconds window rate limit, but still it's 150 requests per minute) and requests can't be traced, so finding the guilty is pretty much impossible, so you're warned I guess... If something like this happen, remove webhook causing problems and it won't be able to send messages anymore.
this
[+]this
.that
[+]that
{{
}}
which are not part of Ingredient, like here: {"embeds": [{"image": {"url": "{{ImageUrl}}"}}]}
➤ {"embeds": [{"image": {"url": "{{ImageUrl}}"} }]}
otherwise they may be assumed as end of Ingredient name and break the validation.<<<
& >>>
around {{Ingredient}}
➤ <<<{{Ingredient}}>>>
(website says <<>>
, but that a typo). Only times when escaping rule may be ignored are when Ingredient is 100% URL and when Ingredient is part of URL and that Ingredient only consists of URL-safe characters, like: https://twitter.com/{{Username}}
(in some cases escaping these were causing broken requests).<>
around links disables that behavior though). So sometimes, instead of building complex request body {"content": "{{Url}}"}
(Url is placeholder, it may be called differently between services) can be more than enough!If you suspect request failed, first check activity logs: Applet ➤ Settings ➤ View Activity. Ff there none errors, just messages about applet being created/updated means it wasn't triggered yet, give it some time, but if there's any please check this troubleshooting list:
Action failure message: Rate limited by the remote server.
- means Discord rate limited this request because IFTTT sends it too frequently. Mostly happens when IFTTT tries to send requests in bulk on same webhook in short amount of time. Discord's webhook rate limit is 5 requests per 2 seconds, keep that in mind.Unable to make web request. Your server returned a 400.
- means request is invalid. Can be caused by:Error 401
- webhook url isn't full, try to copy it again.Error 404
- webhook you're using has been removed. Create new webhook and replace the old URL.Error 405
- happens when you use other than POST methods.There was a problem with the X service.
- and usually no data provided for this one. As it says, the problem is on service side, next check should be successful, if not - try reauthorize.Some services provide close to realtime delay, some are up to 15 minutes, some even longer. Delay in applet description may be misleading and different for everyone, it's always better check by your own. Having multiple applets using same service may cause additional overall delay. Please bare with that as nothing much can be done.
For example for Reddit you may try RSS service as Reddit supports it too (check this), for Twitter/Instagram/etc. you may try services which convert to RSS feed.
IFTTT doesn't have retrospective check, means posts created before applet will be ignored. Check now button performs force check, but for some services it may work differently and don't do any check until cooldown passes.
Here's some services I made for own needs but I think others may find them useful too:
{"avatar_url": "https://avatar.glue-bot.xyz/twitter/{{Username}}"}
Too many requests to this host
error, also applies rate limit so requests will less to fail, allows to use text/plain
header, more info on the page!URL: https://multi-webhook.vercel.app/api/multi
, JSON: {links: [first_url, second_url], ...}
where first_url and second_url are webhook urls.IFTTT Platform allows you to create and publish applets, so you can share it with the others. Also it allows to put script that will execute after applet being triggered, so you can modify output data and skip actions to custom conditions, that script is called Filter code.
Filter code is written in TypeScript, but JavaScript code should work too. If you're relying on type coercion you'll need to manually convert values to right types, code window has build-in linter, it will tell what's wrong.
👉 Update: Before, Filter code was exclusively IFTTT Platform feature, but now it can be used on main website too (Pro subscription required).
// Building JSON body
const body = {
embeds: [{
author: {
name: Trigger.UserName, // equals {{UserName}}
url: Trigger.LinkToTweet // equals {{LinkToTweet}}
},
description: `*${Trigger.Text}*`, // equals *{{Text}}*
color: 0x1da1f2, // equals 1942002 (hex ➤ decimal value)
timestamp: Meta.triggerTime // equals moment.js object but during JSON encoding will be turned to YYYY-MM-DDThh:mm:ss.msZ (discord compatible timestamp)
}]
};
// If you want to manually skip some triggers when some of ingredients match/not match something or allow to run only at certain time you can do this:
if (Trigger.Text.indexOf('skip') > 0) {
MakerWebhooks.makeWebRequest.skip('this reason will appear in activity logs!');
}
// This action makes Web Request and sends webhook
MakerWebhooks.makeWebRequest.setBody(JSON.stringify(body));
// Done!
//== in ts/js quotes around keys can be omitted
let body = {"content": "text"};
// equals
let body = {content: "text"};
//== the difference between single and double quotes that you need to escape it between them own
let text = ['what\'s up?', "*\"winks\"*"];
// equals
let text = ["what's up?", '*"winks"*'];
//== if you want to add value inside string use *template literals*: ` ` with ${}
let message = 'hello, ' + name + '!';
// equals
let message = `hello, ${name}!`;
//== if variable is a string there's no need to use template literals
let zzz = `${ccc}`;
// equals
let zzz = ccc; // if variable is not string but it should be call ccc.toString() on it!
//== var/let/const
// if value might be reassigned, use let
let favoriteNumber = 42
// otherwise use const
const name = 'John'
// using var is ok too, but less preferable
var happy = true
//== check if string includes substring
let str = 'The quick brown fox jumps over the lazy dog';
let substr = 'fox';
let substr2 = 'cat';
let result = str.indexOf(substr) > -1; // equals true
let result2 = str.indexOf(substr2) > -1; // equals false
// explanation: IFTTT doesn't have str.includes(substr) method available
// so we have to use str.indexOf(substr) > -1 instead,
// which returns coordinates of substring and -1 if not found.
// > -1 is in range [0..+inf), >= 0 will work the same way
//== random value
let random = Math.random(); // returns float value in range [0..1) (1 excluded)
let random2 = Math.round(Math.random()); // returns 0 or 1
let random3 = Math.floor(Math.random() * 10); // returns integer value in range [0..9] (10 excluded)
let random4 = 5 + Math.floor(Math.random() * 45); // returns integer value in range [5..49] (50 excluded)
//== random element in array
let arr = ['one', 'two', 'three', 'four'];
let picked = arr[Math.floor(Math.random() * arr.length)];
// check if str is part of substr, optionally comparing them case-insensitively
function includes(str: string, substr: string, caseInsensitive?: boolean): boolean {
if (caseInsensitive) {
str = str.toLowerCase();
substr = substr.toLowerCase();
}
return str.indexOf(substr) > -1;
}
includes('watermelon', 'melon') // returns true
includes('Watermelon', 'Melon') // returns false
includes('Watermelon', 'Melon', true) // returns true
// random integer number between min and max
function random(min: number, max: number): number {
return min + Math.floor(Math.random() * (max - min + 1));
}
random(5, 10) // returns value between 5 and 10
random(-5, 5) // returns value between -5 and 5
// select random element of array
function pick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
pick(['apple', 'banana', 'orange'])
Postman is a GUI tool for sending web requests.
Download it from official website. Available for Windows, Linux, and macOS.
Here's how to use it:
+
on the tabs panel.POST
.Body
tab > raw
> JSON
from the dropdown.Send
.204 No Content
means request succeed!To send attachment(s):
raw
to form-data
.File
.Select Files
and select file (despite it allowing you to choose multiple files, choose one).payload_json
name and json body value (dropdown must say Text
).Insomnia is a GUI tool similar to Postman for sending web requests.
Download it from official website. Available for Windows, Linux, and macOS.
Here's how to use it:
+
on the left, choose New Request
, in dialog window press Create
.POST
.Body
dropdown, choose JSON
.Send
.204 No Content
means request succeed!To send attachment(s):
Body
format from JSON
to Multipart Form
.File
.Choose File
and select file.payload_json
name and json body value (both Text
and Text (Multi-line)
are fine).curl - command line tool for sending web requests.
Also, you can download executable from their website and run it from a directory or make it discoverable through PATH
, so it can be run from anywhere. Using a package manager is preferable as it simplifies the process of installing and updating the tool.
curl -H "Content-Type: application/json" -d <body> <link>
# Usually, request also includes `-X POST` to set request verb to POST, but using `-d` does that automatically.
# -H "Content-Type: application/json" - adds header that tells server you're sending JSON data.
# -d '{"username": "test", "content": "hello"}' - sets request data.
curl -H "Content-Type: application/json" -d '{"username": "test", "content": "hello"}' "https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
# To make command more readable you can split it to multiple lines using backslash `\`
# and/or set webhook url and body as variables.
WEBHOOK_URL="https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
BODY='{"username": "test", "content": "hello"}'
curl \
-H "Content-Type: application/json" \
-d $BODY \
$WEBHOOK_URL
Note: Preinstalled PowerShell (<5.1) comes with curl
command that's actually alias to Invoke-WebRequest
when actual curl can be accessed with curl.exe
. This collision has been resolved in PowerShell Core 6+. If you're unsure which version you're using run this command:
$PSVersionTable.PSVersion.ToString()
# In older version you have to escape " with \ inside strings, so body string be parsed correctly.
curl.exe -H "Content-Type: application/json" -d '{\"username\": \"test\", \"content\": \"hello\"}' "https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
# To improve rediability you can split command with `
# and set webhook url and body to variables
$WEBHOOK_URL = "https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
$BODY = '{\"username\": \"test\", \"content\": \"hello\"}'
# Alternatively, we can use PowerShell hashtables with the ConvertTo-Json function
$BODY = @{ username = "test"; content = "hello" } | ConvertTo-Json
curl.exe `
-H "Content-Type: application/json" `
-d $BODY `
$WEBHOOK_URL
$WEBHOOK_URL = "https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
# In newer versions escaping \ with " is no longer necessary (and makes JSON invalid)
$BODY = '{"username": "test", "content": "hello"}'
# Alternatively, we can use PowerShell hashtables with the ConvertTo-Json function
$BODY = @{ username = "test"; content = "hello" } | ConvertTo-Json
curl.exe -H "Content-Type: application/json" -d $BODY $WEBHOOK_URL
Note: Unlike PowerShell, both curl
and curl.exe
point to the same binary.
REM ^ is multiline splitter in cmd.
curl ^
-H "Content-Type: application/json" ^
-d "{\"username\": \"test\", \"content\": \"hello\"}" ^
https://discord.com/api/webhooks/123/w3bh00k_t0k3n
REM Notice double quotes around body and none around link.
SET WEBHOOK_URL=https://discord.com/api/webhooks/123/w3bh00k_t0k3n
SET BODY="{\"username\": \"test\", \"content\":\"hello\"}"
curl -H "Content-Type: application/json" -d %BODY% %WEBHOOK_URL%
# Adding `-H "Content-Type: multipart/form-data"` is not required as `-F` sets it automatically.
# -F 'payload_json={}' - when sending files JSON can provided with this field.
# -F "file1=@cat.jpg" - adds cat.jpg file as attachment.
# -F "file2=@images/dog.jpg" - adds dog.jpg file from images directory.
curl \
-F 'payload_json={"username": "test", "content": "hello"}' \
-F "file1=@cat.jpg" \
-F "file2=@images/dog.jpg" \
$WEBHOOK_URL
HTTPie is a command line HTTP client, just like curl but more user friendly.
pip
(requires Python 3.x installed). By the way, this is cross-platform solution.Check docs for installation details.
http <url> <body params>
# Optional flags and method were omitted:
# if unspecified method is set to POST if body is specified
# -j/--json forces JSON mode, yet it's default behavior
http "https://discord.com/api/webhooks/123/w3bh00k_t0k3n" content="test" embeds[0][title]="text"
Depends on type of value you have to use different separators:
=
- text.:=
- raw JSON value. Use it for array, number, boolean and nested values.@
- embed file.=@
- embed json file.http \
"https://discord.com/api/webhooks/123/w3bh00k_t0k3n" \
content="test" \
embeds[0][title]="text"
Also, if you don't want to mess with these and would like to just pass raw body, like can be done in curl
, use the next approach:
echo -n '{"content": "test", "embeds": [{"title": "text"}]}' | http "https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
$WEBHOOK_URL = "https://discord.com/api/webhooks/123/w3bh00k_t0k3n"
http $WEBHOOK_URL content="test" embeds[0][title]="text"
# Also you can just pass raw json body:
'{"content": "test", "embeds": [{"title": "text"}]}' | http $WEBHOOK_URL
REM Notice escaped double quotes around values and none around link
SET WEBHOOK_URL=https://discord.com/api/webhooks/123/w3bh00k_t0k3n
http %WEBHOOK_URL% content="test" embeds[0][title]="text"
REM Outer quotes are skipped due to cmd parsing
echo {"content": "test", "embeds": [{"title": "text"}]} | http %WEBHOOK_URL%
# -f flag sets "Content-Type: multipart/form-data" header.
# payload_json='{}' - when sending files json can provided with this field.
# file1@cat.jpg - adds cat.jpg file as attachment.
# file2@images/dog.jpg - adds dog.jpg file from images directory.
http -f $WEBHOOK_URL \
payload_json='{"content": "test", "embeds": [{"title": "text"}]}' \
file1@cat.jpg \
file2@images/dog.jpg
{
"embeds": [{
"color": 2021216,
"title": "New song added!",
"thumbnail": {
"url": "{{AlbumCoverURL}}"
},
"fields":[
{
"name": "Track",
"value": "[{{TrackName}}]({{TrackURL}})",
"inline": true
},
{
"name": "Artist",
"value": "{{ArtistName}}",
"inline": true
},
{
"name": "Album",
"value": "{{AlbumName}}",
"inline": true
}
],
"footer": {
"text": "Added {{SavedAt}}",
"icon_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Spotify_logo_without_text.svg/200px-Spotify_logo_without_text.svg.png"
}
}]
}
{
"content": "{{ChannelUrl}}"
}
Preview will be cached and embeds will show same image.
{
"embeds": [{
"color": 9520895,
"author": {
"name": "{{ChannelName}} is now streaming",
"url": "{{ChannelUrl}}",
"icon_url": "https://avatar-resolver.vercel.app/twitch/{{ChannelName}}"
},
"fields": [
{
"name": ":joystick: Game",
"value": "<<<{{Game}}>>>\u200B",
"inline": true
},
{
"name": ":busts_in_silhouette: Viewers",
"value": "{{CurrentViewers}}",
"inline": true
}
],
"image": { "url": "{{StreamPreview}}" }
}]
}
Fixed issue with cached preview, added game box art.
const body: any = {
embeds: [{
color: 0x9146ff,
author: {
name: `${Trigger.ChannelName} is now streaming`,
url: Trigger.ChannelUrl,
icon_url: `https://avatar-resolver.vercel.app/twitch/${Trigger.ChannelName}`
},
fields: [{
name: ':joystick: Game',
value: Trigger.Game || 'No Game',
inline: true
}, {
name: ':busts_in_silhouette: Viewers',
value: Trigger.CurrentViewers,
inline: true
}],
thumbnail: { url: `https://avatar-resolver.vercel.app/twitch-boxart/${encodeURIComponent(Trigger.Game || '')}` },
image: { url: `${Trigger.StreamPreview}?${+moment()}` },
timestamp: Meta.triggerTime
}]
};
MakerWebhooks.makeWebRequest.setBody(JSON.stringify(body));
Tip:
To filter out text posts it's recommended to use
New post from search
trigger andsubreddit:XYZ self:no
query, whereXYZ
is subreddit name.
{
"content": "{{PostURL}}"
}
Image preview won't be shown and if content has more than 2048 characters, request will fail.
{
"embeds": [{
"color": 16729344,
"author": {
"name": "u/{{Author}}",
"url": "https://www.reddit.com/user/{{Author}}",
"icon_url": "https://avatar-resolver.vercel.app/reddit/{{Author}}"
},
"title": "<<<{{Title}}>>>",
"url": "{{PostURL}}",
"description": "<<<{{Content}}>>>",
"footer": { "text": "r/{{Subreddit}} • Posted at {{PostedAt}}" }
}]
}
"file not found"
placeholder will be shown on text posts.
{
"embeds": [{
"color": 16729344,
"author": {
"name": "u/{{Author}}",
"url": "https://www.reddit.com/user/{{Author}}",
"icon_url": "https://avatar-resolver.vercel.app/reddit/{{Author}}"
},
"title": "<<<{{Title}}>>>",
"url": "{{PostURL}}",
"image": { "url": "{{ImageURL}}" },
"footer": { "text": "r/{{Subreddit}} • Posted at {{PostedAt}}" }
}]
}
Best with link post which are video or GIFs. "allowed_mentions"
will disable everyone/here mentions in content if there any, user/role mentions will still work if you wish to add any. big text posts (over 1k characters).
{
"content": "New Post in **r/{{Subreddit}}** by **u/{{Author}}**\n**<<<{{Title}}>>>**\n<<<{{Content}}>>>",
"allowed_mentions": { "parse": ["users", "roles"] }
}
Adds text to embed for text posts and image for link ones, shortens content so it doesn't break the body
const body: any = {
embeds: [{
color: 0xFF4500,
author: {
name: `u/${Trigger.Author}`,
url: `https://www.reddit.com/user/${Trigger.Author}`,
icon_url: `https://avatar-resolver.vercel.app/reddit/${Trigger.Author}`
},
title: Trigger.Title,
url: Trigger.PostURL,
footer: { text: `r/${Trigger.Subreddit}` },
timestamp: Meta.triggerTime // returns null | use .currentUserTime for now
}]
};
if (/^http\S+$/.test(Trigger.Content)) {
body.embeds[0].image = { url: Trigger.ImageURL };
} else {
body.embeds[0].description = Trigger.Content.slice(0, 2048);
}
MakerWebhooks.makeWebRequest.setBody(JSON.stringify(body));
{
"content": "{{LinkToTweet}}"
}
Custom embed won't show image or video.
{
"embeds": [{
"color": 1942002,
"author": {
"name": "{{UserName}}",
"url": "https://twitter.com/{{UserName}}",
"icon_url": "https://avatar-resolver.vercel.app/twitter/{{UserName}}"
},
"title": "Link",
"url": "{{LinkToTweet}}",
"description": "<<<{{Text}}>>>",
"footer": {
"text": "Posted at {{CreatedAt}}"
}
}]
}
{
"content": "{{Url}}"
}
Embed includes thumbnail without ability to watch it in Discord.
{
"embeds": [{
"color": 16711680,
"author": {
"name": "<<<{{ChannelName}}>>>",
"icon_url": "https://avatar-resolver.vercel.app/youtube-avatar/q?url={{Url}}"
},
"title": "<<<{{Title}}>>>",
"url": "{{Url}}",
"description": "<<<{{Description}}>>>",
"image": { "url": "https://avatar-resolver.vercel.app/youtube-thumbnail/q?url={{Url}}" },
"footer": { "text": "Published at {{PublishedAt}}" }
}]
}
Identically to sending messages with webhooks you can edit previously sent ones. For that you need to have message id which can be copied with right-clicking on the message and selecting Copy Message ID
from context menu (Developer Mode
has to be enabled in user settings). Alternatively, id can be retrieved from response after webhook sending request (wait=true
has to be present in query).
Request URL is https://discord.com/api/webhooks/123/w3bh00k_t0k3n/messages/456
, where 456
is message id.
Request verb has to be set to PATCH
instead of POST
.
Request body is identical to the one we use for sending, but you have to consider next things:
content
and embeds
won't remove them from message, for that you have to pass empty string ("content": ""
) and array ("embeds": []
) respectively.attachments
with empty array in value ("attachments": []
). If you want some of them to persist provide them in the same attachments
array (attachment objects have to be retrieved from previous edit or send responses)Rate limits, or on other words limitation of request rate, allow to limit usage os specific APIs. In case you've been using IFTTT you'll likely encountered rate limit errors as updates from your triggers are sent in bulk with no rate limit handling.
If to sum it up you can send 5 requests per 2 seconds per webhook, failed requests count towards rate limit same as successful ones.
If you're writing or making some thing that would require sending big number of webhook messages you have to implement proper rate limit handling. All webhook links have separate webhooks, so if you have multiple created they won't affect each others. Thankfully, request responses include all needed headers:
X-RateLimit-Limit
- number of requests that can be made during single rate limit windowX-RateLimit-Remaining
- number of remaining requests that can be made (decreases with each request, when it reaches zero next request will likely fail)X-RateLimit-Reset
- Epoch time (seconds since 00:00:00 UTC on January 1, 1970) at which the rate limit resetsX-RateLimit-Reset-After
- Total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond rate limit precisionX-RateLimit-Bucket
- A unique string denoting the rate limit being encountered (non-inclusive of top-level resources in the path)So generally you have to save and check these values before sending new requests so your app won't run into rate limit issues.
Another anti-request fail measure would be retrying request in case it failed, but still relying on response headers.
So usual implementation should be queue to which requests for sending are being added, before each request we have to check response headers, if there's still space for another request (X-RateLimit-Remaining
is greater than zero) perform one, otherwise wait X-RateLimit-Reset-After
seconds or until after X-RateLimit-Reset
time.
Markdown Text 101 from Discord Support. Formatting works in all fields except username
, embed's title
, footer's text
, that means :emoji:
won't work there as well (except it's in Unicode).
2 ways:
User settings
➤ Advanced
➤ Developer Mode
➤ enable. Now you can find id of any user, message, channel or server with right click ➤ Copy ID\
escaping: Put it before (or if you're on iOS, surround with ``) user mention, mentionable role, channel tag or custom emoji and you will get unformatted dataThis thing must be mentioned somewhere because some of you want to use this in messages.
How it writes | How it looks |
---|---|
<@&role-id> | @role-name |
<#channel-id> | #channel-name |
<@user-id> | @mention |
<:custom_emoji_name:emoji-id> | :custom_emoji_name: |
Discord API reference for available tags
Discord added support of dynamic timestamps that allow you to reference specific date and time so it will show dynamically for everyone depending on timezone they're in and Discord language setting. You can generate them by hand (need Unix timestamp) or using one of these websites:
Result string should look like this: <t:1577836800>
or <t:1577836800:t>
Discord API reference for available formatting styles
Discord webhooks support Slack formatting too. Just append /slack
to webhook url to start using it.
Discord | Slack | Comment |
---|---|---|
username | username | |
avatar_url | icon_url | |
content | text | |
embeds | attachments |
Discord | Slack | Comment |
---|---|---|
color | color | supports hex codes "#4c73c7" and has three predefined colors: good (green), warning (yellow) and danger (red). |
author | - | author block declares in different way. See the author table. |
title | title | |
url | title_link | |
description | text | |
fields | fields | |
image | - | image block declares in different way. See the image table. |
thumbnail | - | thumbnail block declares in different way. See the thumbnail table. |
footer | footer | not a block. See the footer table |
timestamp | ts | requires unix timestamp format. |
- | fallback | embed summary for notifications. Not sure if Discord supports that. |
- | pretext | kinda broken atm. Should works as "content" before embed, but it just appends to "description". |
Discord | Slack | Comment |
---|---|---|
name | author_name | just write them inside of attachments like color or etc. |
url | author_link | |
icon_url | author_icon |
Discord | Slack | Comment |
---|---|---|
name | title | |
value | value | |
inline | short |
Discord | Slack | Comment |
---|---|---|
url | image_url | no image block. Just write them inside of attachments like color or etc. |
Discord | Slack | Comment |
---|---|---|
url | thumb_url | no thumbnail block. Just write them inside of attachments like color or etc. |
Discord | Slack | Comment |
---|---|---|
text | footer | no footer block. Just write them inside of attachments like color or etc. |
icon | footer_icon |
Slack formatted example from here:
{
"username": "Webhook",
"icon_url": "https://i.imgur.com/4M34hi2.png",
"text": "Text message. Up to 2000 characters.",
"attachments": [
{
"author_name": "Birdie♫",
"author_link": "https://www.reddit.com/r/cats/",
"author_icon": "https://i.imgur.com/R66g1Pe.jpg",
"title": "Title",
"title_link": "https://google.com/",
"text": "Text message. You can use Markdown here. *Italic* **bold** __underline__ ~~strikeout~~ [hyperlink](https://google.com) `code`",
"color": "#e8d44f",
"fields": [
{
"title": "Text",
"value": "More text",
"short": true
},
{
"title": "Even more text",
"value": "Yup",
"short": true
},
{
"title": "Use `\"inline\": true` parameter, if you want to display fields in the same line.",
"value": "okay..."
},
{
"title": "Thanks!",
"value": "You're welcome :wink:"
}
],
"thumb_url": "https://upload.wikimedia.org/wikipedia/commons/3/38/4-Nature-Wallpapers-2014-1_ukaavUI.jpg",
"image_url": "https://upload.wikimedia.org/wikipedia/commons/5/5a/A_picture_from_China_every_day_108.jpg",
"footer": "Woah! So cool! :smirk:",
"footer_icon": "https://i.imgur.com/fKL31aD.jpg"
}
]
}
Field | Limit |
---|---|
username | 1-80 characters |
content | 2000 characters |
embeds | 10 embed objects |
title * | 256 characters |
description * | 4096 characters |
author.name * | 256 characters |
fields | 25 field objects |
field.name * | 256 characters |
field.value * | 1024 characters |
footer.text * | 2048 characters |
file (not actual field name) | 10 files |
sum of characters in marked fields | 6000 characters |
I hope this guide will help you make cool things for your server. I spent on this guide a lot of time with checking Discord API documentation and testing webhooks with different parameters. The main reason why I made this guide is there was no detailed guide about using webhooks. Existing ones describe using webhooks with known services with pre-made json body without showing how flexible settings are. Existing ones were like copy & paste and nothing more. I do not like that.
If you wanna suggest something, like Applet recipe or something fancy, please, let me know. My Discord tag is birdie0
(was Birdie♫#6017
).
Also I want to say thank next people:
legioncabal
(LEGION#0240
) - grammar fixes and pre-release testingwoody5728
(Delta#5728
) - pre-release testingwolfgang1710
(WolfGang1710#6782
) - pre-release testingdarkpro1337
(DarkPro1337#4304
) - Russian translationThank you all, you are awesome!!!
Repo is public now, if you have any suggestions, fixes or improvements, feel free to open issue or create pull request.
Feel free to contact me on Discord, my Discord tag is birdie0
(was Birdie♫#6017
). If you have no mutual servers with me you can always find me on the r/IFTTT Discord server.
Things that are planned, but not in progress at the moment: