Discord Post ExecModule
Overview
DiscordPostModule creates one message through POST /channels/{channel.id}/messages on Discord API v10. It supports message content, up to ten embed objects, bounded HTTPS links, and an optional reply reference.
Version 2.0 replaces the catalog's empty input/output metadata and unsafe retry model. The module no longer claims that URL values are uploaded as Discord attachments: link_urls are appended to message content, while actual binary file upload remains outside this module. A legacy attachments array is accepted as the same bounded link list for workflow compatibility.
Message creation is an outbound, non-idempotent action. The module validates the full request before transport, disables automatic mentions by default, requires a valid Discord message identifier in the success response, and never retries HTTP 429, 5xx, timeout, or ambiguous network failures automatically.
Usage
- Create a Discord application and bot, then add the bot to the target server.
- Grant the bot
View ChannelandSend Messages; grantEmbed Linkswhen using embeds. - Store the bot token in the encrypted
apiKeyfield of a ValkyrAIIntegrationAccount, set the account toREADY, and bind it throughExecModuleConfig.authConfig.integrationAccount. - Add
DiscordPostModuleto a workflow and setchannel_id. - Provide non-empty
content, at least one embed, or at least one HTTPS link. - Require outbound approval where policy demands it, then inspect the returned message ID before continuing.
Inputs
| Name | Type | Required | Description | Constraints |
|---|---|---|---|---|
channel_id | string | Yes | Discord channel snowflake. | 17–20 decimal digits. |
content | string | Conditional | Message text. | Content plus appended links must be at most 2,000 Unicode code points. |
embeds | array of objects | Conditional | Discord embed objects passed to API v10. | At most ten objects; Discord performs final field and aggregate embed validation. |
link_urls | array of strings | Conditional | HTTPS links appended to content on separate lines. | At most ten; no URL credentials or fragments; each URL is at most 2,048 characters. |
reply_to_message_id | string | No | Existing message in the target channel to reply to. | 17–20 decimal digits. Missing referenced messages do not make Discord reject the create request. |
At least one of content, embeds, or link_urls is required. Legacy attachments is accepted as an alias for link_urls; it does not perform multipart file upload.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
discord.message.id | string | Success | Message snowflake returned by Discord. |
discord.message.channel_id | string | Success | Provider channel identifier, verified against the requested channel. |
discord.message.status | string | Always | SENT after a validated success response or ERROR on failure. |
The module also marks the runtime ExecModule GOOD or ERROR and emits bounded EventLog progress. Bot tokens and provider response bodies are never written to WorkflowState or failure events.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | Discord REST API v10 |
| Authentication | Bot token |
| Status | READY |
accountName | Human-readable Discord bot identity |
apiKey | Encrypted bot token used as Authorization: Bot ... |
| Relationship | Bind through ExecModuleConfig.authConfig.integrationAccount |
The bot must be a member of the server and authorized for the selected channel. Never place the token in payload fields, message content, logs, URL query parameters, or documentation examples.
Configuration
Illustrative normalized configuration:
{
"version": "2.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:discord-release-bot"
},
"payloadConfig": {
"parameters": "{\"channel_id\":\"123456789012345678\",\"content\":\"Release complete.\",\"link_urls\":[\"https://status.example.com/releases/42\"],\"reply_to_message_id\":\"345678901234567890\"}"
}
}
The account value is a symbolic secure reference. Persisted workflows use the generated IntegrationAccount relationship, not a plaintext token.
The default transport has a 10-second connection timeout and a 30-second read timeout. Message creation is not retried automatically because a timeout or upstream error may occur after Discord committed the message.
Operations
DiscordPostModule performs one create-message operation with compatible shapes:
| Shape | Provider payload | Side effect |
|---|---|---|
| Text message | content | Creates one channel message. |
| Embed message | embeds with optional content | Creates one message containing up to ten embeds. |
| Link message | link_urls appended to content | Creates one message containing HTTPS links; no files are uploaded. |
| Reply | Any shape plus message_reference | Creates one message referencing another message in the target channel. |
Multipart attachments, editing, deletion, reactions, threads, components, polls, scheduled sends, bulk sends, and webhook impersonation are separate operations and are not implemented here.
Errors and Failure Modes
| Failure | Cause | Retry guidance |
|---|---|---|
| Validation failure | Missing content, malformed snowflake, non-array embeds or links, too many items, unsafe URL, or length overflow. | Correct the configuration; no Discord request was sent. |
| IntegrationAccount failure | No bound account, non-READY account, or blank apiKey. | Bind or repair the Discord bot account; no Discord request was sent. |
| HTTP 400/404 | Invalid payload, channel, reply reference, or provider constraint. | Correct the request before a new attempt. |
| HTTP 401/403 | Invalid token, missing bot membership, or insufficient channel permissions. | Rotate or reauthorize the IntegrationAccount and bot permissions. |
| HTTP 429 | Discord rate limit. | Reconcile whether the message exists, honor provider rate-limit guidance, then schedule a deliberate new attempt. |
| HTTP 5xx or network failure | Provider or transport failure with an ambiguous commit boundary. | Do not retry blindly; check the channel first. |
| Invalid success response | A 2xx response lacks a snowflake id, has a different channel_id, or is not valid JSON. | Treat as failure and inspect provider compatibility; success is not recorded. |
Provider response bodies can contain user content or credential-like details, so failures expose only a sanitized HTTP status or bounded local validation message.
Example
Create a deployment notification with one embed and one release link:
{
"channel_id": "123456789012345678",
"content": "Release complete.",
"embeds": [
{
"title": "Release 42",
"description": "Production verification passed.",
"color": 5793266
}
],
"link_urls": ["https://status.example.com/releases/42"]
}
Expected result shape after Discord returns a valid message object:
{
"discord.message.id": "234567890123456789",
"discord.message.channel_id": "123456789012345678",
"discord.message.status": "SENT"
}
Notes
- Pagination: not applicable; one execution attempts one message creation.
- Limits: local validation enforces the 2,000-code-point content limit, ten embeds, ten links, and bounded link lengths. Discord applies final embed, payload, permission, and account limits.
- Mentions:
allowed_mentions.parseis empty, so content does not automatically expand@everyone, role, or user mentions. - Rate limits: HTTP 429 is surfaced without retry. Discord may return provider-specific rate-limit headers, but the module does not expose or persist them.
- Idempotency: message creation is non-idempotent. A workflow retry can create a duplicate message, and this module does not claim durable deduplication across ambiguous failures.
- API constraints: reply references are scoped to the configured channel and use
fail_if_not_exists: false; Discord can create the message without a reply link when the referenced message is absent. - Destructive behavior: creation is an external outbound write. This module cannot delete or compensate a message; removal requires a separately authorized operation.
- Observability: EventLogs record validation, submission, success ID, or sanitized failure. Message content, bot tokens, and provider bodies are excluded from failure logs.
- Unverified boundary: deterministic tests cover account binding, exact request construction, validation, response mapping, mention suppression, non-retry behavior, metadata serialization, and secret redaction. Live Discord execution requires separately authorized service credentials and is not exercised in repository tests.
See Discord's official Create Message resource and permissions reference for current provider behavior.