Discord API
Connect a validated incoming webhook and publish text, attachments, polls, forum threads, TTS, video links, and rich embeds.
Current RelayAPI capability
| Property | RelayAPI behavior |
|---|---|
| Platform key | discord |
| Connection | Discord incoming-webhook bearer URL; no OAuth |
| Text | Up to 2,000 characters |
| Attachments | Up to 10 non-video media items, 10 MiB each |
| Video | Up to 10 URLs appended for Discord to unfurl; not uploaded as attachments |
| Embeds | Up to 10, with Discord's per-field and 6,000 combined-text limits |
| Polls | 2–10 answers, optional multiselect, up to 768 hours |
| Forum/media channels | Create a thread or execute in an existing thread |
| Scheduling | Yes, through RelayAPI |
| Native analytics endpoints | No |
Connect an incoming webhook
Create an incoming webhook in the destination channel, then connect its URL:
curl -X POST https://api.relayapi.dev/v1/connect/discord \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "ws_example",
"webhook_url": "https://discord.com/api/webhooks/123456789/secret-token"
}'RelayAPI accepts only Discord's exact HTTPS origin and incoming-webhook path (an optional Discord API version segment is allowed). Credentials in the URL authority, custom ports, queries, fragments, unrelated hosts, and non-incoming webhook types are rejected. RelayAPI calls Discord's Get Webhook with Token endpoint and requires a channel-bound incoming webhook before encrypting the URL.
The webhook URL contains its authentication token. Treat the whole URL like a password: do not expose it in logs, screenshots, browser history, post content, or source control.
Publish a message
curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: example-idempotency-key" \
-d '{
"content": "Deployment completed.",
"targets": ["acc_discord_example"],
"scheduled_at": "now",
"target_options": {
"discord": {
"username": "Release Bot",
"avatar_url": "https://cdn.example.com/release-bot.png"
}
}
}'RelayAPI executes the webhook with wait=true and reports success only when Discord returns a message ID. When guild and channel information is available, the target result also includes a Discord jump URL.
Attachments and video URLs
Non-video media is fetched through RelayAPI's public-URL checks and streamed to Discord as multipart/form-data. Each attachment is capped at Discord's documented default of 10 MiB because an incoming webhook does not expose the destination's higher upload tier.
{
"content": "New design files",
"targets": ["acc_discord_example"],
"media": [
{"url": "https://cdn.example.com/design.png", "type": "image"},
{"url": "https://cdn.example.com/walkthrough.mp4", "type": "video"}
],
"scheduled_at": "now"
}Video items are not downloaded and uploaded. Up to ten video URLs are appended to content so Discord can unfurl them. The original content plus every appended video URL must still fit the 2,000-character message limit.
Rich embeds
{
"targets": ["acc_discord_example"],
"scheduled_at": "now",
"target_options": {
"discord": {
"embeds": [
{
"title": "Release 2.0",
"description": "The release is live.",
"url": "https://example.com/changelog",
"color": 5814783,
"footer": {"text": "RelayAPI"}
}
]
}
}
}RelayAPI validates the Discord limits it can determine before sending: 10 embeds, title 256, description 4,096, footer 2,048, author 256, 25 fields per embed, field name 256, field value 1,024, and 6,000 text characters across all embeds. Discord remains authoritative for other embed fields.
target_options.discord
| Field | Type | Behavior |
|---|---|---|
content | string | Discord-specific content override |
media | array | Discord-specific media override |
username | string | Per-message webhook name, at most 80 characters |
avatar_url | URL | Per-message webhook avatar |
embeds | object array | Up to 10 Discord rich embeds |
tts | boolean | Request text-to-speech; true requires non-empty message content |
thread_id | snowflake | Execute the webhook in an existing forum/media thread |
thread_name | string | Create a new forum/media thread, 1–100 characters; mutually exclusive with thread_id |
applied_tags | snowflake[] | Up to five tags for a newly created thread_name; not valid with thread_id |
poll | object | Question, 2–10 answers, optional duration/multiselect/layout; see below |
A request must produce at least one of content, an embed, a poll, or an uploaded attachment. A video URL counts as content after RelayAPI appends it.
Poll and forum-thread example
await client.posts.create({
content: 'Pick a release window',
targets: ['discord'],
scheduled_at: 'now',
target_options: {
discord: {
thread_name: 'Release planning',
applied_tags: ['123456789012345678'],
poll: {
question: { text: 'When should we deploy?' },
answers: [
{ poll_media: { text: 'Tuesday' } },
{ poll_media: { text: 'Thursday', emoji: { name: '🚀' } } },
],
duration: 24,
allow_multiselect: false,
layout_type: 1,
},
},
},
});Poll questions are 1–300 characters, answer text is 1–55 characters, and an
answer emoji contains exactly one of id or name. duration is a whole
number of hours from 1 through 768. Discord remains authoritative for channel
type, thread permissions, available tags, and whether TTS is enabled.
Published message editing
RelayAPI can edit text for a Discord message created by the connected webhook, both as a published post target and as a persisted outbound inbox message. It does not retarget the webhook or edit attachments. For a message executed in an existing or newly created forum/media thread, RelayAPI persists the exact thread ID from the confirmed publish result and supplies it to Discord during the edit. A thread-scoped record without a valid durable thread ID fails closed before provider I/O; RelayAPI does not guess a channel or thread target. Each provider write uses an idempotency key and durable operation; see Published Edits and Social Actions.
Lifecycle and retries
- Discord webhook credentials do not refresh. If the webhook is deleted or its token is regenerated, connect the replacement URL.
- Discord rate-limit responses preserve
Retry-After; reuse the original RelayAPI idempotency key and exact body for a safe retry. - A timeout after Discord may have accepted a message is not a reason to mint a new key. Incoming-webhook execution has no correlation-safe lookup for an ambiguous create, so inspect the RelayAPI target outcome and do not automatically replay it as a new logical message.
- Disconnecting the RelayAPI account does not promise deletion of the webhook in Discord; remove it in Discord when retiring the credential.
Official Discord references
Found something wrong? Help us improve this page.