RelayAPI

Discord API

Connect a validated incoming webhook and publish text, attachments, polls, forum threads, TTS, video links, and rich embeds.

Current RelayAPI capability

PropertyRelayAPI behavior
Platform keydiscord
ConnectionDiscord incoming-webhook bearer URL; no OAuth
TextUp to 2,000 characters
AttachmentsUp to 10 non-video media items, 10 MiB each
VideoUp to 10 URLs appended for Discord to unfurl; not uploaded as attachments
EmbedsUp to 10, with Discord's per-field and 6,000 combined-text limits
Polls2–10 answers, optional multiselect, up to 768 hours
Forum/media channelsCreate a thread or execute in an existing thread
SchedulingYes, through RelayAPI
Native analytics endpointsNo

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

FieldTypeBehavior
contentstringDiscord-specific content override
mediaarrayDiscord-specific media override
usernamestringPer-message webhook name, at most 80 characters
avatar_urlURLPer-message webhook avatar
embedsobject arrayUp to 10 Discord rich embeds
ttsbooleanRequest text-to-speech; true requires non-empty message content
thread_idsnowflakeExecute the webhook in an existing forum/media thread
thread_namestringCreate a new forum/media thread, 1–100 characters; mutually exclusive with thread_id
applied_tagssnowflake[]Up to five tags for a newly created thread_name; not valid with thread_id
pollobjectQuestion, 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.

On this page