Ray
PricingDocsSign inStart free
Browse the docs

Getting started

Ray documentationAuthenticationUsing Ray with AI coding agentsMCP serverTypeScript SDK

Guides

Sending notificationsIdempotencyTemplatesScheduling and test sendsStatus and feedsClick trackingDelivery webhooks

Channels

Email with Amazon SESEmail with SMTPPush notifications with Firebase Cloud MessagingSlack notificationsDiscord notificationsTelegram notificationsSMS with TwilioSMS with sendsms.mnHTTPS webhook channel

Reference

Rate limits and quotasErrors

Getting started

Ray documentationAuthenticationUsing Ray with AI coding agentsMCP serverTypeScript SDK

Guides

Sending notificationsIdempotencyTemplatesScheduling and test sendsStatus and feedsClick trackingDelivery webhooks

Channels

Email with Amazon SESEmail with SMTPPush notifications with Firebase Cloud MessagingSlack notificationsDiscord notificationsTelegram notificationsSMS with TwilioSMS with sendsms.mnHTTPS webhook channel

Reference

Rate limits and quotasErrors

Channels

Discord notifications

Post Ray notifications to a Discord channel through a channel webhook. Discord setup, config, markdown content, mention safety, escaping and errors.

View as Markdownllms.txtAPI reference

The discord_webhook channel posts messages to a Discord text channel through a Discord webhook URL. The webhook decides the channel, so there is no per-message recipient. API calls go to https://ray-api.gege.mn with Authorization: Bearer $RAY_API_KEY.

Channel type discord_webhook
Template kind discord_text
Recipient {}
Pacing None (Discord rate limits are retried)
Suppression None

What you need from Discord

You need the Manage Webhooks permission on the server.

  1. In Discord open Server Settings, Integrations, Webhooks.
  2. Click New Webhook, give it a name (shown as the message author) and pick the channel.
  3. Click Copy Webhook URL. It looks like https://discord.com/api/webhooks/123456789012345678/AbCdEf....

The token in the URL is the secret: anyone with it can post to the channel. For several channels, create one webhook and one Ray channel config per channel.

Channel config

Create the config in the dashboard: Channels, New channel, Discord webhook. The URL is stored encrypted; after saving, the dashboard only shows its host.

Field Required Notes
Name yes Internal label, for example release-notes.
Webhook URL (webhookUrl) yes Must be on discord.com or discordapp.com. Any other host is refused when sending (the delivery fails as failed_terminal). When editing, leave blank to keep the stored URL.

Find the config's id with GET /channels; its kind is discord_webhook.

Template content

Field Type Notes
content string, 1 to 2000 Discord markdown: **bold**, *italic*, __underline__, ~~strike~~, `code`, > quote, # heading, - list, [text](https://example.com).

Escaping: Ray puts a backslash before \ ` * _ ~ | > # - in param values, so a value renders literally and can't add formatting.

Mentions are always disabled: Ray sends every message with Discord's allowed_mentions set to parse nothing, so @everyone, @here, user and role mentions render as plain text and ping nobody, whether they come from the template or from params.

Recipient

Always an empty object:

{}

Send a message

curl -X POST https://ray-api.gege.mn/send \
  -H "Authorization: Bearer $RAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channelConfigId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "content": {
      "content": "**Release {{version}}** is out\n> {{summary}}\n[Changelog](https://example.com/changelog)"
    },
    "params": { "version": "2.4.0", "summary": "Faster exports and a new dark theme" },
    "recipient": {}
  }'
const res = await fetch("https://ray-api.gege.mn/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RAY_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "release-2.4.0-discord",
  },
  body: JSON.stringify({
    channelConfigId: "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    templateId: "1d2e3f4a-5b6c-4d7e-8f9a-0b1c2d3e4f5a", // a discord_text template
    params: { version: "2.4.0", summary: "Faster exports and a new dark theme" },
    recipient: {},
  }),
});
if (res.status !== 202) throw new Error(`Ray ${res.status}: ${await res.text()}`);

Note the nesting: the send's content object has a single field that is also called content.

With escaping, the 2.4.0 param renders as-is, while a summary like - fixed *all* bugs would appear literally instead of as a list item with italics.

Discord returns no message id, so the delivery row's providerMessageId is discord-<rowId>. delivered means Discord accepted the message.

Limits

Discord caps message content at 2000 characters and rate-limits webhooks. Ray does not pace Discord sends; when Discord answers 429, the delivery is retried with backoff. For bursts of many messages to one channel, combine them into one message or use priority: "low" for the non-urgent ones.

Errors

Errors appear on the delivery row as providerError: { "name": "DiscordWebhookError", "message": "Discord webhook failed: <status> <statusText> <body>" }.

Result Cases
failed_terminal (not retried) Any HTTP 4xx except 429, such as 401 Invalid Webhook Token, 404 Unknown Webhook (webhook deleted), 400 invalid body (for example content over 2000 characters after rendering); a URL that isn't on discord.com or discordapp.com or isn't valid
failed_retryable (retried) HTTP 429, HTTP 5xx, network errors and timeouts

Common fixes:

  • Unknown Webhook: the webhook was deleted in Discord. Create a new one and update the config.
  • 400 with a content length error: rendered params pushed the message over 2000 characters. Shorten the template or the params.
  • refusing to POST to non-Discord host: the configured URL isn't a Discord webhook URL.

Request errors from POST /send are listed in Errors.

PreviousSlack notificationsNextTelegram notifications

On this page

  • What you need from Discord
  • Channel config
  • Template content
  • Recipient
  • Send a message
  • Limits
  • Errors
Ray

Notification delivery for teams that already have providers.

Product

PricingDocsAPI referenceMCP serverTypeScript SDKAgent skillsllms.txt

Legal

TermsPrivacyRefunds

Contact

[email protected]

© 2026 L’Atelier Gege LLC