Forest Logo
search
package_2

rCord

By @bfzdk

Roblox

rCord

A Roblox Luau library for sending Discord webhook messages. Provides a chainable builder API for constructing messages and embeds, built-in validation against Discord's limits, and automatic rate limit handling.

Features

  • Chainable message and embed builders
  • Full embed support (fields, images, thumbnails, author, footer, etc.)
  • Validation against all Discord character/count limits before sending
  • Automatic retry on rate limit (429) responses
  • Secret type support for safe URL storage
  • allowed_mentions control to prevent accidental pings

Proxy Requirement

Discord blocks incoming requests from Roblox's servers, so you cannot call the Discord webhook API directly. You must route requests through a proxy server that forwards them to Discord on your behalf.

Set your webhook URL to your proxy's endpoint instead of the Discord URL:

-- Proxy URL — use this instead
local webhook = rCord.Webhook.new("https://your-proxy.example.com/api/webhooks/1234/abcd")

The proxy should forward the request to Discord verbatim, preserving the method, headers, and body. A common self-hosted option is discord-proxy by lewisakura. For a free setup using Cloudflare Workers, see this DevForum tutorial.


Quick Start

local rCord = require(path.to.rCord)

local webhook = rCord.Webhook.new("https://your-proxy.example.com/api/webhooks/...")

-- Simple message
webhook:send("Hello from Roblox!")

-- Message with an embed
local embed = rCord.Embed.new()
    :setTitle("Round Over")
    :setDescription("The round has ended.")
    :setColor(Color3.fromRGB(88, 101, 242))

local message = rCord.Message.new()
    :setUsername("Game Bot")
    :addEmbed(embed)

local success, response = webhook:send(message)
if not success then
    warn(response.error)
end

API Reference

rCord

The top-level table returned by the module.

ExportTypeDescription
WebhookclassCreates and sends webhook messages
MessageclassBuilds a message payload
EmbedclassBuilds an embed object
FlagstableNamed constants for message flags
setDebug(value)functionEnables/disables debug logging to output

rCord.Flags

ConstantValueDescription
SUPPRESS_EMBEDS4Prevents Discord from generating link previews
SUPPRESS_NOTIFICATIONS4096Sends the message silently (no ping sound)

Webhook

Webhook.new(url: string | Secret) -> Webhook

Creates a new webhook instance.

-- Plain string
local webhook = rCord.Webhook.new("https://your-proxy.example.com/api/webhooks/...")

-- Roblox Secret (recommended — keeps URL out of output)
local webhook = rCord.Webhook.new(HttpService:GetSecret("DISCORD_WEBHOOK"))

webhook:send(body, wait?, thread_id?) -> (boolean, ResponseData)

Sends a message. body can be a plain string or a Message object.

Automatically retries once if Discord responds with a 429 rate limit, waiting the duration Discord specifies.

ParameterTypeDescription
bodystring | MessageThe message to send. A plain string is wrapped in a Message automatically.
waitboolean?If true, Discord waits for the message to be created before responding. Required if you need the message ID back. Defaults to false.
thread_idstring?Sends the message into a specific thread inside the webhook's channel.
local success, response = webhook:send("Hello!", true)

if success then
    print(response.statusCode)   -- 200
    print(response.body.id)      -- message ID (body is auto-decoded from JSON)
else
    warn(response.error)
end

Message

Builds the payload sent to Discord. All setters return self for chaining.

Message.new() -> Message

local message = rCord.Message.new()

Setters

MethodParameterDiscord limitDescription
:setContent(content)string2000 charsThe text content of the message
:setUsername(username)stringOverrides the webhook's display name
:setAvatarUrl(url)stringOverrides the webhook's avatar
:setTTS(tts)booleanSends as a text-to-speech message
:setThreadName(name)stringCreates a new thread with this name (forum/media channels)
:setAllowedMentions(body)AllowedMentionsControls which mentions actually ping
:setFlags(flags)numberBitfield of message flags (use rCord.Flags)
:addEmbed(embed)Embed | EmbedType10 embedsAdds an embed to the message

message:validate() -> (boolean, string?)

Validates the message against Discord's limits. Called automatically by send — you only need this if you want to check before sending.

local ok, err = message:validate()
if not ok then
    warn(err)  -- e.g. "over 2000 characters"
end

message:toJSON() -> table

Returns a plain table representation suitable for JSON encoding. Called internally by send.

AllowedMentions type

webhook:send(
    rCord.Message.new()
        :setContent("Hey @everyone!")
        :setAllowedMentions({ parse = {} })  -- parse = {} suppresses all pings
)
FieldTypeDescription
parse{"roles" | "users" | "everyone"}?Which mention types to parse. Omit a type to suppress it.
roles{string}?Allowlist of role IDs to ping (max 100)
users{string}?Allowlist of user IDs to ping (max 100)
replied_userboolean?Whether to ping the user being replied to

Embed

Builds a Discord embed. All setters return self for chaining.

Embed.new() -> Embed

local embed = rCord.Embed.new()

Setters

MethodParameterDiscord limitDescription
:setTitle(title)string256 charsBold title at the top of the embed
:setDescription(description)string4096 charsMain body text
:setUrl(url)stringMakes the title a hyperlink
:setTimestamp(timestamp)stringISO 8601 timestamp shown in the footer
:setColor(color)number | Color3Left-side accent colour. Accepts a Color3 or a decimal integer
:setFooter(body)EmbedFooter2048 chars (text)Footer text and optional icon
:setImage(body)EmbedImageLarge image at the bottom
:setThumbnail(body)EmbedThumbnailSmall image on the right
:setAuthor(body)EmbedAuthor256 chars (name)Author line at the top
:setProvider(body)EmbedProviderProvider info (usually set by Discord, not bots)
:setType(type)"rich" | "image" | ...Embed type. Use "rich" for custom embeds
:addField(body)EmbedField25 fields, 256/1024 charsAdds a name/value field

:addField(body: EmbedField)

embed:addField({
    name = "Score",
    value = "1500",
    inline = true,
})
FieldTypeLimitDescription
namestring256 charsField label
valuestring1024 charsField content
inlineboolean?Whether to display side-by-side with adjacent inline fields

:setColor(color: number | Color3)

Both forms are accepted:

embed:setColor(0x5865F2)                     -- hex integer
embed:setColor(Color3.fromRGB(88, 101, 242)) -- Color3

:setFooter(body: EmbedFooter)

embed:setFooter({ text = "rCord", icon_url = "https://..." })

:setAuthor(body: EmbedAuthor)

embed:setAuthor({ name = "PlayerName", icon_url = "https://...", url = "https://..." })

:setImage(body: EmbedImage) / :setThumbnail(body: EmbedThumbnail)

embed:setImage({ url = "https://..." })
embed:setThumbnail({ url = "https://...", width = 64, height = 64 })

embed:validate() -> (boolean, string?)

Validates all fields against Discord's character limits. Called automatically via message:validate().

embed:getCharacters() -> number

Returns the total character count of the embed (title + description + footer text + author name + all field names and values). Discord's combined limit across all embeds in a message is 6000.


ResponseData

Returned as the second value from webhook:send().

FieldTypeDescription
successbooleanWhether Discord accepted the message (2xx status)
statusCodenumberHTTP status code
statusMessagestringHTTP status message
bodyany?Decoded response body. Contains the created message object as a table when wait = true. nil when Discord returns no content (e.g. wait = false). Falls back to the raw string if the body is not valid JSON.
retry_afternumber?Seconds Discord asked to wait (present on 429 responses)
errorstring?Error description when the request or validation failed

Examples

Logging a player event

local rCord = require(path.to.rCord)
local webhook = rCord.Webhook.new(HttpService:GetSecret("LOG_WEBHOOK"))

local function logPlayerJoin(player)
    local embed = rCord.Embed.new()
        :setAuthor({ name = player.Name })
        :setTitle("Player Joined")
        :setColor(Color3.fromRGB(87, 242, 135))
        :setTimestamp(DateTime.now():ToIsoDate())

    webhook:send(rCord.Message.new():addEmbed(embed))
end

Silent notification with suppressed pings

local message = rCord.Message.new()
    :setContent("Server restarting in 60 seconds.")
    :setAllowedMentions({ parse = {} })
    :setFlags(rCord.Flags.SUPPRESS_NOTIFICATIONS)

webhook:send(message)

Multiple inline fields

local embed = rCord.Embed.new()
    :setTitle("Match Results")
    :addField({ name = "Winner", value = "TeamA", inline = true })
    :addField({ name = "Score", value = "5 - 2", inline = true })
    :addField({ name = "Duration", value = "12m 34s", inline = true })
    :setColor(0xFEE75C)

webhook:send(rCord.Message.new():addEmbed(embed))

Checking the response

local success, response = webhook:send("Test", true)

if not success then
    warn("[rCord]", response.error, response.statusCode)
    return
end

-- body is already decoded — no JSONDecode needed
print("Message ID:", response.body.id)

Package Details

Install command (Click to copy)


Version

0.1.4

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.