Discord API
Schedule and automate Discord messages with RelayAPI — text messages, image embeds, video links, custom usernames, avatars, and rich embeds via webhooks.
Quick Reference
| Property | Value |
|---|---|
| Platform key | discord |
| Auth method | Webhook URL |
| Text limit | 2,000 characters |
| Images per message | 10 (as embeds) |
| Videos per message | Appended as links (auto-embedded by Discord) |
| Image formats | JPEG, PNG, GIF, WebP |
| Image max size | 25 MB |
| Video max size | 25 MB |
| Message types | Text, Image Embeds, Video Links, Rich Embeds |
| Scheduling | Yes |
| Analytics | No |
SDK source — TypeScript · Python · Go · Java · REST API
Before You Start
Discord integration uses webhook URLs — no OAuth needed. The webhook URL itself contains the authentication token. Create a webhook in your Discord server's channel settings (Edit Channel > Integrations > Webhooks). Images are sent as embeds (up to 10 per message). Videos are appended to the message content as URLs and auto-embedded by Discord. You can customize the username and avatar per message.
Quick Start
Send a message to a Discord channel:
import Relay from '@relayapi/sdk';
const client = new Relay();
const post = await client.posts.create({
content: 'Hello from RelayAPI!',
targets: ['discord'],
scheduled_at: 'now',
});
console.log(post.id); // post_abc123from relay import Relay
client = Relay()
post = client.posts.create(
content='Hello from RelayAPI!',
targets=['discord'],
scheduled_at='now',
)
print(post.id) # post_abc123client := relaygo.NewClient()
post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Hello from RelayAPI!"),
Targets: relaygo.F([]string{"discord"}),
ScheduledAt: relaygo.F("now"),
})
fmt.Println(post.ID) // post_abc123RelayClient client = RelayOkHttpClient.fromEnv();
PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Hello from RelayAPI!")
.addTarget("discord")
.scheduledAt("now")
.build());
System.out.println(post.id()); // post_abc123curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from RelayAPI!",
"targets": ["discord"],
"scheduled_at": "now"
}'Content Types
Text Message
Plain text message up to 2,000 characters.
const post = await client.posts.create({
content: 'Hello from RelayAPI!',
targets: ['discord'],
scheduled_at: 'now',
});post = client.posts.create(
content='Hello from RelayAPI!',
targets=['discord'],
scheduled_at='now',
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Hello from RelayAPI!"),
Targets: relaygo.F([]string{"discord"}),
ScheduledAt: relaygo.F("now"),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Hello from RelayAPI!")
.addTarget("discord")
.scheduledAt("now")
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from RelayAPI!",
"targets": ["discord"],
"scheduled_at": "now"
}'Message with Custom Username and Avatar
Override the webhook's default name and avatar for a specific message.
const post = await client.posts.create({
content: 'Server maintenance starting in 30 minutes.',
targets: ['discord'],
scheduled_at: 'now',
target_options: {
discord: {
username: 'Maintenance Bot',
avatar_url: 'https://cdn.example.com/bot-avatar.png',
},
},
});post = client.posts.create(
content='Server maintenance starting in 30 minutes.',
targets=['discord'],
scheduled_at='now',
target_options={
'discord': {
'username': 'Maintenance Bot',
'avatar_url': 'https://cdn.example.com/bot-avatar.png',
},
},
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Server maintenance starting in 30 minutes."),
Targets: relaygo.F([]string{"discord"}),
ScheduledAt: relaygo.F("now"),
TargetOptions: relaygo.F(map[string]interface{}{
"discord": map[string]interface{}{
"username": "Maintenance Bot",
"avatar_url": "https://cdn.example.com/bot-avatar.png",
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Server maintenance starting in 30 minutes.")
.addTarget("discord")
.scheduledAt("now")
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("discord", JsonValue.from(Map.of(
"username", "Maintenance Bot",
"avatar_url", "https://cdn.example.com/bot-avatar.png"
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Server maintenance starting in 30 minutes.",
"targets": ["discord"],
"scheduled_at": "now",
"target_options": {
"discord": {
"username": "Maintenance Bot",
"avatar_url": "https://cdn.example.com/bot-avatar.png"
}
}
}'Image Message
Images are sent as embeds. Up to 10 images per message.
const post = await client.posts.create({
content: 'Check out these screenshots!',
targets: ['discord'],
media: [
{ url: 'https://cdn.example.com/screenshot1.png', type: 'image' },
{ url: 'https://cdn.example.com/screenshot2.png', type: 'image' },
],
scheduled_at: 'now',
});post = client.posts.create(
content='Check out these screenshots!',
targets=['discord'],
media=[
{'url': 'https://cdn.example.com/screenshot1.png', 'type': 'image'},
{'url': 'https://cdn.example.com/screenshot2.png', 'type': 'image'},
],
scheduled_at='now',
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Check out these screenshots!"),
Targets: relaygo.F([]string{"discord"}),
Media: relaygo.F([]relaygo.PostNewParamsMedia{
{URL: relaygo.F("https://cdn.example.com/screenshot1.png"), Type: relaygo.F(relaygo.PostNewParamsMediaTypeImage)},
{URL: relaygo.F("https://cdn.example.com/screenshot2.png"), Type: relaygo.F(relaygo.PostNewParamsMediaTypeImage)},
}),
ScheduledAt: relaygo.F("now"),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Check out these screenshots!")
.addTarget("discord")
.addMedia(PostCreateParams.Media.builder()
.url("https://cdn.example.com/screenshot1.png")
.type(PostCreateParams.Media.Type.IMAGE)
.build())
.addMedia(PostCreateParams.Media.builder()
.url("https://cdn.example.com/screenshot2.png")
.type(PostCreateParams.Media.Type.IMAGE)
.build())
.scheduledAt("now")
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out these screenshots!",
"targets": ["discord"],
"media": [
{"url": "https://cdn.example.com/screenshot1.png", "type": "image"},
{"url": "https://cdn.example.com/screenshot2.png", "type": "image"}
],
"scheduled_at": "now"
}'Video Message
Videos are appended as URLs in the message content. Discord auto-embeds supported video links.
const post = await client.posts.create({
content: 'Watch our latest update!',
targets: ['discord'],
media: [
{ url: 'https://cdn.example.com/video.mp4', type: 'video' },
],
scheduled_at: 'now',
});post = client.posts.create(
content='Watch our latest update!',
targets=['discord'],
media=[
{'url': 'https://cdn.example.com/video.mp4', 'type': 'video'},
],
scheduled_at='now',
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Watch our latest update!"),
Targets: relaygo.F([]string{"discord"}),
Media: relaygo.F([]relaygo.PostNewParamsMedia{
{URL: relaygo.F("https://cdn.example.com/video.mp4"), Type: relaygo.F(relaygo.PostNewParamsMediaTypeVideo)},
}),
ScheduledAt: relaygo.F("now"),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Watch our latest update!")
.addTarget("discord")
.addMedia(PostCreateParams.Media.builder()
.url("https://cdn.example.com/video.mp4")
.type(PostCreateParams.Media.Type.VIDEO)
.build())
.scheduledAt("now")
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Watch our latest update!",
"targets": ["discord"],
"media": [
{"url": "https://cdn.example.com/video.mp4", "type": "video"}
],
"scheduled_at": "now"
}'Rich Embeds
Send structured rich embeds with titles, descriptions, colors, images, and more.
const post = await client.posts.create({
targets: ['discord'],
scheduled_at: 'now',
target_options: {
discord: {
embeds: [
{
title: 'New Release: v2.0',
description: 'We just shipped a major update with new features!',
url: 'https://example.com/changelog',
color: 5814783,
image: { url: 'https://cdn.example.com/banner.png' },
footer: { text: 'RelayAPI' },
timestamp: '2026-04-01T12:00:00Z',
},
],
},
},
});post = client.posts.create(
targets=['discord'],
scheduled_at='now',
target_options={
'discord': {
'embeds': [
{
'title': 'New Release: v2.0',
'description': 'We just shipped a major update with new features!',
'url': 'https://example.com/changelog',
'color': 5814783,
'image': {'url': 'https://cdn.example.com/banner.png'},
'footer': {'text': 'RelayAPI'},
'timestamp': '2026-04-01T12:00:00Z',
},
],
},
},
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Targets: relaygo.F([]string{"discord"}),
ScheduledAt: relaygo.F("now"),
TargetOptions: relaygo.F(map[string]interface{}{
"discord": map[string]interface{}{
"embeds": []map[string]interface{}{
{
"title": "New Release: v2.0",
"description": "We just shipped a major update with new features!",
"url": "https://example.com/changelog",
"color": 5814783,
"image": map[string]interface{}{"url": "https://cdn.example.com/banner.png"},
"footer": map[string]interface{}{"text": "RelayAPI"},
"timestamp": "2026-04-01T12:00:00Z",
},
},
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.addTarget("discord")
.scheduledAt("now")
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("discord", JsonValue.from(Map.of(
"embeds", List.of(Map.of(
"title", "New Release: v2.0",
"description", "We just shipped a major update with new features!",
"url", "https://example.com/changelog",
"color", 5814783,
"image", Map.of("url", "https://cdn.example.com/banner.png"),
"footer", Map.of("text", "RelayAPI"),
"timestamp", "2026-04-01T12:00:00Z"
))
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"targets": ["discord"],
"scheduled_at": "now",
"target_options": {
"discord": {
"embeds": [
{
"title": "New Release: v2.0",
"description": "We just shipped a major update with new features!",
"url": "https://example.com/changelog",
"color": 5814783,
"image": {"url": "https://cdn.example.com/banner.png"},
"footer": {"text": "RelayAPI"},
"timestamp": "2026-04-01T12:00:00Z"
}
]
}
}
}'Text with Rich Embeds
Combine regular text content with rich embeds.
const post = await client.posts.create({
content: '📢 Announcement',
targets: ['discord'],
scheduled_at: 'now',
target_options: {
discord: {
embeds: [
{
title: 'Scheduled Maintenance',
description: 'We will be performing maintenance on April 5th from 2-4 AM UTC.',
color: 16776960,
},
],
},
},
});post = client.posts.create(
content='📢 Announcement',
targets=['discord'],
scheduled_at='now',
target_options={
'discord': {
'embeds': [
{
'title': 'Scheduled Maintenance',
'description': 'We will be performing maintenance on April 5th from 2-4 AM UTC.',
'color': 16776960,
},
],
},
},
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("📢 Announcement"),
Targets: relaygo.F([]string{"discord"}),
ScheduledAt: relaygo.F("now"),
TargetOptions: relaygo.F(map[string]interface{}{
"discord": map[string]interface{}{
"embeds": []map[string]interface{}{
{
"title": "Scheduled Maintenance",
"description": "We will be performing maintenance on April 5th from 2-4 AM UTC.",
"color": 16776960,
},
},
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("📢 Announcement")
.addTarget("discord")
.scheduledAt("now")
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("discord", JsonValue.from(Map.of(
"embeds", List.of(Map.of(
"title", "Scheduled Maintenance",
"description", "We will be performing maintenance on April 5th from 2-4 AM UTC.",
"color", 16776960
))
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "📢 Announcement",
"targets": ["discord"],
"scheduled_at": "now",
"target_options": {
"discord": {
"embeds": [
{
"title": "Scheduled Maintenance",
"description": "We will be performing maintenance on April 5th from 2-4 AM UTC.",
"color": 16776960
}
]
}
}
}'Media Requirements
Images
| Property | Requirement |
|---|---|
| Max per message | 10 (as embeds) |
| Formats | JPEG, PNG, GIF, WebP |
| Max file size | 25 MB |
Videos
| Property | Requirement |
|---|---|
| Max per message | 1 (as URL in content) |
| Formats | MP4, WebM, MOV |
| Max file size | 25 MB |
| Delivery | Appended to message content as URL |
target_options Fields
All fields go inside target_options.discord on your post request.
| Field | Type | Default | Description |
|---|---|---|---|
content | string | — | Override message content for Discord specifically |
media | object[] | — | Override media for Discord specifically |
username | string | — | Override the webhook's display name for this message |
avatar_url | string | — | Override the webhook's avatar for this message |
embeds | object[] | — | Rich embed objects (up to 10). Fields: title, description, url, color, image, thumbnail, footer, timestamp |
Common Errors
| Error | Cause | Fix |
|---|---|---|
| Content too long | Text exceeds 2,000 characters | Shorten the content. Use target_options.discord.content for a shorter version. |
| Invalid webhook URL | Webhook URL is malformed or deleted | Create a new webhook in the Discord channel settings (Edit Channel > Integrations > Webhooks). |
| Empty message | No content or embeds provided | Add text content or at least one embed. |
| Too many embeds | More than 10 embeds | Reduce to 10 or fewer embeds per message. |
Known Quirks
- No OAuth required — Discord webhooks are self-authenticating via the URL. Keep webhook URLs secret.
- Custom username and avatar per message — each message can have a different display name and avatar.
- Images are embeds — images are sent as embed objects, not direct attachments.
- Videos are auto-embedded — video URLs in message content are automatically embedded by Discord if the format is supported.
- Embed color is an integer — use decimal color values (e.g.,
5814783for#58B9FF). - No direct message URL — webhook responses don't include a jump URL to the message.
- Rate limits — Discord enforces rate limits on webhooks (30 messages per 60 seconds per channel).
- Markdown supported — Discord messages support a subset of Markdown:
**bold**,*italic*,~~strikethrough~~,`code`,> quote.
Automations
Discord automation uses the bot token as a single credential. All send actions target a channel, not a user — for DMs, pre-create the DM channel with the bot and store the channel_id as the contact's Discord identifier.
Triggers
| Type | Fires on |
|---|---|
discord_message | Guild channel message |
discord_dm | DM to the bot |
discord_reaction | Emoji reaction added |
discord_member_joined | New member joins a guild |
discord_thread_created | Thread opened in a channel |
discord_interaction | Slash command / button / select interaction |
Send nodes
Endpoint: POST https://discord.com/api/v10/channels/{channel-id}/messages (+ variants).
| Node | Required fields |
|---|---|
discord_send_message | content, optional channel_id (else from state/contact) |
discord_send_embed | embeds[] (≤10) |
discord_send_components | components[], optional content |
discord_send_attachment | url, optional content (URL is embedded in content for unfurl) |
discord_react | emoji, message_id |
discord_edit_message | content, message_id |
discord_start_thread | name (≤100), optional auto_archive_duration, message_id |
Found something wrong? Help us improve this page.