Octopus.Script exported 2026-07-20 by Meanski belongs to ‘Discord’ category.
Posts a message and/or rich embed to a Discord channel via a webhook. No Discord application or bot is required — create a webhook in the channel’s settings (Integrations → Webhooks) and paste its URL into this step.
The embed can automatically colour itself green or red based on the deployment outcome (set Color to auto and use this step with a “run always” condition), include Project/Release/Environment fields, and link back to the deployment in Octopus.
Mentions (@everyone, roles, users) in message content are suppressed by default to prevent accidental pings from interpolated release notes.
Sets the output variable MessageId for use in later steps.
Requires curl and either jq or python3 on the worker.
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
Webhook URL
DiscordNotify.WebhookUrl =
The Discord webhook URL (Channel Settings → Integrations → Webhooks). Consider binding this to a sensitive project variable (e.g. #{Discord.WebhookUrl}) so it can be scoped per environment and rotated in one place.
Message Content
DiscordNotify.Content =
Plain message text (supports Discord markdown). Optional when Include Embed is enabled. Maximum 2000 characters — longer values are truncated.
Username Override
DiscordNotify.Username =
Overrides the webhook’s configured display name. Leave blank to use the name configured on the webhook.
Avatar URL Override
DiscordNotify.AvatarUrl =
Overrides the webhook’s configured avatar image. Leave blank to use the avatar configured on the webhook.
Include Embed
DiscordNotify.IncludeEmbed = True
If enabled, sends a rich embed with the title, description, colour, timestamp, a link to the deployment, and optional deployment detail fields.
Embed Title
DiscordNotify.EmbedTitle = #{Octopus.Project.Name} release #{Octopus.Release.Number} deployed to #{Octopus.Environment.Name}
Title of the embed. Maximum 256 characters — longer values are truncated.
Embed Description
DiscordNotify.EmbedDescription =
Body text of the embed (supports Discord markdown). Maximum 4096 characters — longer values are truncated.
Color
DiscordNotify.Color = auto
Colour of the embed’s side bar. auto picks green or red based on whether the deployment has an error — useful with a “run always” step condition. A raw decimal colour value is also accepted.
Include Deployment Fields
DiscordNotify.IncludeDeploymentFields = True
If enabled, adds Project, Release, and Environment fields to the embed (fields without a value, e.g. Release during a runbook run, are omitted).
Suppress Mentions
DiscordNotify.SuppressMentions = True
If enabled, @everyone, role, and user mentions in the message content will not ping anyone. Disable only if you intentionally want the notification to ping.
Thread ID
DiscordNotify.ThreadId =
Optional ID of a thread (or forum post) in the webhook’s channel to post into. Leave blank to post to the channel itself.
Script body
Steps based on this template will execute the following Bash script.
#!/bin/bash
# Discord - Send Notification
# Posts a message and/or rich embed to a Discord channel via a webhook.
# API reference: https://discord.com/developers/docs/resources/webhook#execute-webhook
# Read step parameters
WebhookUrl=$(get_octopusvariable "DiscordNotify.WebhookUrl")
Content=$(get_octopusvariable "DiscordNotify.Content")
Username=$(get_octopusvariable "DiscordNotify.Username")
AvatarUrl=$(get_octopusvariable "DiscordNotify.AvatarUrl")
IncludeEmbed=$(get_octopusvariable "DiscordNotify.IncludeEmbed")
EmbedTitle=$(get_octopusvariable "DiscordNotify.EmbedTitle")
EmbedDescription=$(get_octopusvariable "DiscordNotify.EmbedDescription")
EmbedColor=$(get_octopusvariable "DiscordNotify.Color")
IncludeFields=$(get_octopusvariable "DiscordNotify.IncludeDeploymentFields")
SuppressMentions=$(get_octopusvariable "DiscordNotify.SuppressMentions")
ThreadId=$(get_octopusvariable "DiscordNotify.ThreadId")
# System variables used for the auto colour, detail fields, and deployment link
SysProjectName=$(get_octopusvariable "Octopus.Project.Name")
SysReleaseNumber=$(get_octopusvariable "Octopus.Release.Number")
SysEnvironmentName=$(get_octopusvariable "Octopus.Environment.Name")
SysDeploymentError=$(get_octopusvariable "Octopus.Deployment.Error")
SysServerUri=$(get_octopusvariable "Octopus.Web.ServerUri")
SysDeploymentLink=$(get_octopusvariable "Octopus.Web.DeploymentLink")
# Validate required tooling
if ! command -v curl >/dev/null 2>&1; then
echo "ERROR: curl is required on the worker to run this step." >&2
exit 1
fi
if command -v jq >/dev/null 2>&1; then
JSON_TOOL="jq"
elif command -v python3 >/dev/null 2>&1; then
JSON_TOOL="python3"
else
echo "ERROR: jq or python3 is required on the worker to build and parse JSON." >&2
exit 1
fi
# Prints the JSON-encoded (quoted) form of $1
json_escape() {
if [[ "$JSON_TOOL" == "jq" ]]; then
jq -cn --arg v "$1" '$v'
else
python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$1"
fi
}
# Reads JSON on stdin and prints the value at dotted path $1 (empty if missing or null)
json_get() {
if [[ "$JSON_TOOL" == "jq" ]]; then
jq -r ".$1 // empty" 2>/dev/null
else
python3 -c '
import json, sys
try:
data = json.load(sys.stdin)
except Exception:
sys.exit(0)
cur = data
for part in sys.argv[1].split("."):
if isinstance(cur, dict) and part in cur:
cur = cur[part]
else:
sys.exit(0)
if cur is None:
sys.exit(0)
print(cur if not isinstance(cur, (dict, list)) else json.dumps(cur))
' "$1"
fi
}
# Truncates $1 to at most $2 characters, appending an ellipsis when cut
truncate_to() {
local s="$1" max="$2"
if (( ${#s} > max )); then
echo "${s:0:$((max - 1))}…"
else
echo "$s"
fi
}
# Validate required parameters
if [[ -z "$WebhookUrl" ]]; then
echo "ERROR: WebhookUrl is required." >&2
exit 1
fi
if [[ ! "$WebhookUrl" =~ ^https://(ptb\.|canary\.)?(discord|discordapp)\.com/api/webhooks/ ]]; then
echo "ERROR: WebhookUrl does not look like a Discord webhook URL (expected https://discord.com/api/webhooks/...)." >&2
exit 1
fi
if [[ -z "$Content" && "$IncludeEmbed" != "True" ]]; then
echo "ERROR: Nothing to send. Provide Content, or enable Include Embed." >&2
exit 1
fi
# Build the payload
PARTS=()
if [[ -n "$Content" ]]; then
Content=$(truncate_to "$Content" 2000)
PARTS+=("\"content\":$(json_escape "$Content")")
fi
[[ -n "$Username" ]] && PARTS+=("\"username\":$(json_escape "$Username")")
[[ -n "$AvatarUrl" ]] && PARTS+=("\"avatar_url\":$(json_escape "$AvatarUrl")")
if [[ "$SuppressMentions" == "True" ]]; then
PARTS+=("\"allowed_mentions\":{\"parse\":[]}")
fi
if [[ "$IncludeEmbed" == "True" ]]; then
EPARTS=()
if [[ -n "$EmbedTitle" ]]; then
EmbedTitle=$(truncate_to "$EmbedTitle" 256)
EPARTS+=("\"title\":$(json_escape "$EmbedTitle")")
fi
if [[ -n "$EmbedDescription" ]]; then
EmbedDescription=$(truncate_to "$EmbedDescription" 4096)
EPARTS+=("\"description\":$(json_escape "$EmbedDescription")")
fi
# Link the embed title back to the deployment when running in a deployment context
if [[ -n "$SysServerUri" && -n "$SysDeploymentLink" ]]; then
EPARTS+=("\"url\":$(json_escape "${SysServerUri}${SysDeploymentLink}")")
fi
# Resolve the embed colour (Discord expects a decimal integer)
case "$(echo "$EmbedColor" | tr '[:upper:]' '[:lower:]')" in
auto)
# Green unless the deployment has an error
if [[ -n "$SysDeploymentError" ]]; then COLOR=15548997; else COLOR=5763719; fi
;;
success) COLOR=5763719 ;;
failure) COLOR=15548997 ;;
warning) COLOR=16705372 ;;
info|"") COLOR=5793266 ;;
*)
if [[ "$EmbedColor" =~ ^[0-9]+$ ]]; then
COLOR="$EmbedColor"
else
echo "WARNING: Unrecognised Color '$EmbedColor'; using info blue." >&2
COLOR=5793266
fi
;;
esac
EPARTS+=("\"color\":$COLOR")
EPARTS+=("\"timestamp\":\"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\"")
if [[ "$IncludeFields" == "True" ]]; then
FIELDS=()
[[ -n "$SysProjectName" ]] && FIELDS+=("{\"name\":\"Project\",\"value\":$(json_escape "$SysProjectName"),\"inline\":true}")
[[ -n "$SysReleaseNumber" ]] && FIELDS+=("{\"name\":\"Release\",\"value\":$(json_escape "$SysReleaseNumber"),\"inline\":true}")
[[ -n "$SysEnvironmentName" ]] && FIELDS+=("{\"name\":\"Environment\",\"value\":$(json_escape "$SysEnvironmentName"),\"inline\":true}")
if (( ${#FIELDS[@]} > 0 )); then
EPARTS+=("\"fields\":[$(IFS=,; echo "${FIELDS[*]}")]")
fi
fi
PARTS+=("\"embeds\":[{$(IFS=,; echo "${EPARTS[*]}")}]")
fi
PAYLOAD="{$(IFS=,; echo "${PARTS[*]}")}"
# Build the URL. wait=true makes Discord return the created message so the
# message id can be captured as an output variable.
POST_URL="${WebhookUrl}?wait=true"
if [[ -n "$ThreadId" ]]; then
POST_URL="${POST_URL}&thread_id=${ThreadId}"
fi
# Send, retrying on rate limiting (HTTP 429)
echo "Sending Discord notification..."
[[ -n "$Content" ]] && echo " Content : $(truncate_to "$Content" 60)"
[[ "$IncludeEmbed" == "True" && -n "$EmbedTitle" ]] && echo " Embed : $EmbedTitle"
[[ -n "$ThreadId" ]] && echo " Thread : $ThreadId"
echo ""
MAX_ATTEMPTS=3
ATTEMPT=1
while true; do
RAW=$(curl --silent --show-error --max-time 60 --write-out $'\n%{http_code}' \
--request POST "$POST_URL" \
--header "Content-Type: application/json" \
--data "$PAYLOAD")
CURL_EXIT=$?
if [[ $CURL_EXIT -ne 0 ]]; then
echo "ERROR: curl request failed (exit code $CURL_EXIT)." >&2
exit 1
fi
HTTP_STATUS=$(echo "$RAW" | tail -n 1)
RESPONSE=$(echo "$RAW" | sed '$d')
if [[ "$HTTP_STATUS" == "429" ]]; then
if (( ATTEMPT >= MAX_ATTEMPTS )); then
echo "ERROR: Discord rate limited the webhook and retries were exhausted." >&2
exit 1
fi
RETRY_AFTER=$(echo "$RESPONSE" | json_get "retry_after")
# retry_after can be fractional seconds; round up in plain bash
WHOLE="${RETRY_AFTER%%.*}"
if [[ "$WHOLE" =~ ^[0-9]+$ ]]; then
WAIT_SECONDS=$((WHOLE + 1))
else
WAIT_SECONDS=2
fi
echo "Rate limited (429); retrying in ${WAIT_SECONDS}s (attempt $((ATTEMPT + 1))/${MAX_ATTEMPTS})..."
sleep "$WAIT_SECONDS"
ATTEMPT=$((ATTEMPT + 1))
continue
fi
break
done
if [[ "$HTTP_STATUS" -ge 400 ]]; then
ERROR_MSG=$(echo "$RESPONSE" | json_get "message")
ERROR_CODE=$(echo "$RESPONSE" | json_get "code")
echo "ERROR: Discord API returned an error (HTTP $HTTP_STATUS)." >&2
[[ -n "$ERROR_CODE" ]] && echo " Code : $ERROR_CODE" >&2
[[ -n "$ERROR_MSG" ]] && echo " Message: $ERROR_MSG" >&2
exit 1
fi
MESSAGE_ID=$(echo "$RESPONSE" | json_get "id")
echo "Notification sent successfully."
[[ -n "$MESSAGE_ID" ]] && echo " Message ID: $MESSAGE_ID"
set_octopusvariable "MessageId" "$MESSAGE_ID"
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "5adb6692-04b1-4475-800b-fbf0652696ed",
"Name": "Discord - Send Notification",
"Description": "Posts a message and/or rich embed to a [Discord](https://discord.com) channel via a [webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks). No Discord application or bot is required — create a webhook in the channel's settings (Integrations → Webhooks) and paste its URL into this step.\n\nThe embed can automatically colour itself green or red based on the deployment outcome (set Color to `auto` and use this step with a \"run always\" condition), include Project/Release/Environment fields, and link back to the deployment in Octopus.\n\nMentions (`@everyone`, roles, users) in message content are suppressed by default to prevent accidental pings from interpolated release notes.\n\nSets the output variable `MessageId` for use in later steps.\n\nRequires `curl` and either `jq` or `python3` on the worker.",
"Version": 1,
"ExportedAt": "2026-07-20T11:46:55.000Z",
"ActionType": "Octopus.Script",
"Author": "Meanski",
"Packages": [],
"Parameters": [
{
"Id": "6f5e5a78-6498-4102-ba10-357f5c1bd3fe",
"Name": "DiscordNotify.WebhookUrl",
"Label": "Webhook URL",
"HelpText": "The Discord webhook URL (Channel Settings → Integrations → Webhooks). Consider binding this to a sensitive project variable (e.g. `#{Discord.WebhookUrl}`) so it can be scoped per environment and rotated in one place.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Id": "198f797b-fd12-4a25-b203-c6d3bf49b428",
"Name": "DiscordNotify.Content",
"Label": "Message Content",
"HelpText": "Plain message text (supports Discord markdown). Optional when Include Embed is enabled. Maximum 2000 characters — longer values are truncated.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "MultiLineText"
}
},
{
"Id": "6469aad0-ac4e-4a5d-a115-b1940bc6642a",
"Name": "DiscordNotify.Username",
"Label": "Username Override",
"HelpText": "Overrides the webhook's configured display name. Leave blank to use the name configured on the webhook.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "b3609271-4cd3-4f63-96e7-a6726dd65828",
"Name": "DiscordNotify.AvatarUrl",
"Label": "Avatar URL Override",
"HelpText": "Overrides the webhook's configured avatar image. Leave blank to use the avatar configured on the webhook.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "bc4073e3-43de-470f-9d21-20f25b9ede5c",
"Name": "DiscordNotify.IncludeEmbed",
"Label": "Include Embed",
"HelpText": "If enabled, sends a rich embed with the title, description, colour, timestamp, a link to the deployment, and optional deployment detail fields.",
"DefaultValue": "True",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "4b3767ac-56f1-4f93-8245-12bf07d09ed0",
"Name": "DiscordNotify.EmbedTitle",
"Label": "Embed Title",
"HelpText": "Title of the embed. Maximum 256 characters — longer values are truncated.",
"DefaultValue": "#{Octopus.Project.Name} release #{Octopus.Release.Number} deployed to #{Octopus.Environment.Name}",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "61aa08a7-c638-49ce-b800-9ceb67ce8bac",
"Name": "DiscordNotify.EmbedDescription",
"Label": "Embed Description",
"HelpText": "Body text of the embed (supports Discord markdown). Maximum 4096 characters — longer values are truncated.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "MultiLineText"
}
},
{
"Id": "644b2cad-e521-4a1b-a1ab-287278f4d84e",
"Name": "DiscordNotify.Color",
"Label": "Color",
"HelpText": "Colour of the embed's side bar. `auto` picks green or red based on whether the deployment has an error — useful with a \"run always\" step condition. A raw decimal colour value is also accepted.",
"DefaultValue": "auto",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "auto|Auto (green on success, red on failure)\nsuccess|Green\nfailure|Red\nwarning|Yellow\ninfo|Blue"
}
},
{
"Id": "0bc69655-4c3d-43a9-af89-c084d1fed3e5",
"Name": "DiscordNotify.IncludeDeploymentFields",
"Label": "Include Deployment Fields",
"HelpText": "If enabled, adds Project, Release, and Environment fields to the embed (fields without a value, e.g. Release during a runbook run, are omitted).",
"DefaultValue": "True",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "da6f5ef7-5c60-44c3-98fb-f727f6e11bdd",
"Name": "DiscordNotify.SuppressMentions",
"Label": "Suppress Mentions",
"HelpText": "If enabled, `@everyone`, role, and user mentions in the message content will not ping anyone. Disable only if you intentionally want the notification to ping.",
"DefaultValue": "True",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "06f05ca7-2f1c-4aa9-86ae-4eba79084040",
"Name": "DiscordNotify.ThreadId",
"Label": "Thread ID",
"HelpText": "Optional ID of a thread (or forum post) in the webhook's channel to post into. Leave blank to post to the channel itself.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
}
],
"Properties": {
"Octopus.Action.Script.ScriptSource": "Inline",
"Octopus.Action.Script.Syntax": "Bash",
"Octopus.Action.Script.ScriptBody": "#!/bin/bash\n\n# Discord - Send Notification\n# Posts a message and/or rich embed to a Discord channel via a webhook.\n# API reference: https://discord.com/developers/docs/resources/webhook#execute-webhook\n\n# Read step parameters\nWebhookUrl=$(get_octopusvariable \"DiscordNotify.WebhookUrl\")\nContent=$(get_octopusvariable \"DiscordNotify.Content\")\nUsername=$(get_octopusvariable \"DiscordNotify.Username\")\nAvatarUrl=$(get_octopusvariable \"DiscordNotify.AvatarUrl\")\nIncludeEmbed=$(get_octopusvariable \"DiscordNotify.IncludeEmbed\")\nEmbedTitle=$(get_octopusvariable \"DiscordNotify.EmbedTitle\")\nEmbedDescription=$(get_octopusvariable \"DiscordNotify.EmbedDescription\")\nEmbedColor=$(get_octopusvariable \"DiscordNotify.Color\")\nIncludeFields=$(get_octopusvariable \"DiscordNotify.IncludeDeploymentFields\")\nSuppressMentions=$(get_octopusvariable \"DiscordNotify.SuppressMentions\")\nThreadId=$(get_octopusvariable \"DiscordNotify.ThreadId\")\n\n# System variables used for the auto colour, detail fields, and deployment link\nSysProjectName=$(get_octopusvariable \"Octopus.Project.Name\")\nSysReleaseNumber=$(get_octopusvariable \"Octopus.Release.Number\")\nSysEnvironmentName=$(get_octopusvariable \"Octopus.Environment.Name\")\nSysDeploymentError=$(get_octopusvariable \"Octopus.Deployment.Error\")\nSysServerUri=$(get_octopusvariable \"Octopus.Web.ServerUri\")\nSysDeploymentLink=$(get_octopusvariable \"Octopus.Web.DeploymentLink\")\n\n# Validate required tooling\n\nif ! command -v curl >/dev/null 2>&1; then\n echo \"ERROR: curl is required on the worker to run this step.\" >&2\n exit 1\nfi\n\nif command -v jq >/dev/null 2>&1; then\n JSON_TOOL=\"jq\"\nelif command -v python3 >/dev/null 2>&1; then\n JSON_TOOL=\"python3\"\nelse\n echo \"ERROR: jq or python3 is required on the worker to build and parse JSON.\" >&2\n exit 1\nfi\n\n# Prints the JSON-encoded (quoted) form of $1\njson_escape() {\n if [[ \"$JSON_TOOL\" == \"jq\" ]]; then\n jq -cn --arg v \"$1\" '$v'\n else\n python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' \"$1\"\n fi\n}\n\n# Reads JSON on stdin and prints the value at dotted path $1 (empty if missing or null)\njson_get() {\n if [[ \"$JSON_TOOL\" == \"jq\" ]]; then\n jq -r \".$1 // empty\" 2>/dev/null\n else\n python3 -c '\nimport json, sys\ntry:\n data = json.load(sys.stdin)\nexcept Exception:\n sys.exit(0)\ncur = data\nfor part in sys.argv[1].split(\".\"):\n if isinstance(cur, dict) and part in cur:\n cur = cur[part]\n else:\n sys.exit(0)\nif cur is None:\n sys.exit(0)\nprint(cur if not isinstance(cur, (dict, list)) else json.dumps(cur))\n' \"$1\"\n fi\n}\n\n# Truncates $1 to at most $2 characters, appending an ellipsis when cut\ntruncate_to() {\n local s=\"$1\" max=\"$2\"\n if (( ${#s} > max )); then\n echo \"${s:0:$((max - 1))}…\"\n else\n echo \"$s\"\n fi\n}\n\n# Validate required parameters\n\nif [[ -z \"$WebhookUrl\" ]]; then\n echo \"ERROR: WebhookUrl is required.\" >&2\n exit 1\nfi\n\nif [[ ! \"$WebhookUrl\" =~ ^https://(ptb\\.|canary\\.)?(discord|discordapp)\\.com/api/webhooks/ ]]; then\n echo \"ERROR: WebhookUrl does not look like a Discord webhook URL (expected https://discord.com/api/webhooks/...).\" >&2\n exit 1\nfi\n\nif [[ -z \"$Content\" && \"$IncludeEmbed\" != \"True\" ]]; then\n echo \"ERROR: Nothing to send. Provide Content, or enable Include Embed.\" >&2\n exit 1\nfi\n\n# Build the payload\n\nPARTS=()\n\nif [[ -n \"$Content\" ]]; then\n Content=$(truncate_to \"$Content\" 2000)\n PARTS+=(\"\\\"content\\\":$(json_escape \"$Content\")\")\nfi\n\n[[ -n \"$Username\" ]] && PARTS+=(\"\\\"username\\\":$(json_escape \"$Username\")\")\n[[ -n \"$AvatarUrl\" ]] && PARTS+=(\"\\\"avatar_url\\\":$(json_escape \"$AvatarUrl\")\")\n\nif [[ \"$SuppressMentions\" == \"True\" ]]; then\n PARTS+=(\"\\\"allowed_mentions\\\":{\\\"parse\\\":[]}\")\nfi\n\nif [[ \"$IncludeEmbed\" == \"True\" ]]; then\n EPARTS=()\n\n if [[ -n \"$EmbedTitle\" ]]; then\n EmbedTitle=$(truncate_to \"$EmbedTitle\" 256)\n EPARTS+=(\"\\\"title\\\":$(json_escape \"$EmbedTitle\")\")\n fi\n\n if [[ -n \"$EmbedDescription\" ]]; then\n EmbedDescription=$(truncate_to \"$EmbedDescription\" 4096)\n EPARTS+=(\"\\\"description\\\":$(json_escape \"$EmbedDescription\")\")\n fi\n\n # Link the embed title back to the deployment when running in a deployment context\n if [[ -n \"$SysServerUri\" && -n \"$SysDeploymentLink\" ]]; then\n EPARTS+=(\"\\\"url\\\":$(json_escape \"${SysServerUri}${SysDeploymentLink}\")\")\n fi\n\n # Resolve the embed colour (Discord expects a decimal integer)\n case \"$(echo \"$EmbedColor\" | tr '[:upper:]' '[:lower:]')\" in\n auto)\n # Green unless the deployment has an error\n if [[ -n \"$SysDeploymentError\" ]]; then COLOR=15548997; else COLOR=5763719; fi\n ;;\n success) COLOR=5763719 ;;\n failure) COLOR=15548997 ;;\n warning) COLOR=16705372 ;;\n info|\"\") COLOR=5793266 ;;\n *)\n if [[ \"$EmbedColor\" =~ ^[0-9]+$ ]]; then\n COLOR=\"$EmbedColor\"\n else\n echo \"WARNING: Unrecognised Color '$EmbedColor'; using info blue.\" >&2\n COLOR=5793266\n fi\n ;;\n esac\n EPARTS+=(\"\\\"color\\\":$COLOR\")\n\n EPARTS+=(\"\\\"timestamp\\\":\\\"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\\\"\")\n\n if [[ \"$IncludeFields\" == \"True\" ]]; then\n FIELDS=()\n [[ -n \"$SysProjectName\" ]] && FIELDS+=(\"{\\\"name\\\":\\\"Project\\\",\\\"value\\\":$(json_escape \"$SysProjectName\"),\\\"inline\\\":true}\")\n [[ -n \"$SysReleaseNumber\" ]] && FIELDS+=(\"{\\\"name\\\":\\\"Release\\\",\\\"value\\\":$(json_escape \"$SysReleaseNumber\"),\\\"inline\\\":true}\")\n [[ -n \"$SysEnvironmentName\" ]] && FIELDS+=(\"{\\\"name\\\":\\\"Environment\\\",\\\"value\\\":$(json_escape \"$SysEnvironmentName\"),\\\"inline\\\":true}\")\n if (( ${#FIELDS[@]} > 0 )); then\n EPARTS+=(\"\\\"fields\\\":[$(IFS=,; echo \"${FIELDS[*]}\")]\")\n fi\n fi\n\n PARTS+=(\"\\\"embeds\\\":[{$(IFS=,; echo \"${EPARTS[*]}\")}]\")\nfi\n\nPAYLOAD=\"{$(IFS=,; echo \"${PARTS[*]}\")}\"\n\n# Build the URL. wait=true makes Discord return the created message so the\n# message id can be captured as an output variable.\n\nPOST_URL=\"${WebhookUrl}?wait=true\"\nif [[ -n \"$ThreadId\" ]]; then\n POST_URL=\"${POST_URL}&thread_id=${ThreadId}\"\nfi\n\n# Send, retrying on rate limiting (HTTP 429)\n\necho \"Sending Discord notification...\"\n[[ -n \"$Content\" ]] && echo \" Content : $(truncate_to \"$Content\" 60)\"\n[[ \"$IncludeEmbed\" == \"True\" && -n \"$EmbedTitle\" ]] && echo \" Embed : $EmbedTitle\"\n[[ -n \"$ThreadId\" ]] && echo \" Thread : $ThreadId\"\necho \"\"\n\nMAX_ATTEMPTS=3\nATTEMPT=1\n\nwhile true; do\n RAW=$(curl --silent --show-error --max-time 60 --write-out $'\\n%{http_code}' \\\n --request POST \"$POST_URL\" \\\n --header \"Content-Type: application/json\" \\\n --data \"$PAYLOAD\")\n\n CURL_EXIT=$?\n\n if [[ $CURL_EXIT -ne 0 ]]; then\n echo \"ERROR: curl request failed (exit code $CURL_EXIT).\" >&2\n exit 1\n fi\n\n HTTP_STATUS=$(echo \"$RAW\" | tail -n 1)\n RESPONSE=$(echo \"$RAW\" | sed '$d')\n\n if [[ \"$HTTP_STATUS\" == \"429\" ]]; then\n if (( ATTEMPT >= MAX_ATTEMPTS )); then\n echo \"ERROR: Discord rate limited the webhook and retries were exhausted.\" >&2\n exit 1\n fi\n RETRY_AFTER=$(echo \"$RESPONSE\" | json_get \"retry_after\")\n # retry_after can be fractional seconds; round up in plain bash\n WHOLE=\"${RETRY_AFTER%%.*}\"\n if [[ \"$WHOLE\" =~ ^[0-9]+$ ]]; then\n WAIT_SECONDS=$((WHOLE + 1))\n else\n WAIT_SECONDS=2\n fi\n echo \"Rate limited (429); retrying in ${WAIT_SECONDS}s (attempt $((ATTEMPT + 1))/${MAX_ATTEMPTS})...\"\n sleep \"$WAIT_SECONDS\"\n ATTEMPT=$((ATTEMPT + 1))\n continue\n fi\n\n break\ndone\n\nif [[ \"$HTTP_STATUS\" -ge 400 ]]; then\n ERROR_MSG=$(echo \"$RESPONSE\" | json_get \"message\")\n ERROR_CODE=$(echo \"$RESPONSE\" | json_get \"code\")\n echo \"ERROR: Discord API returned an error (HTTP $HTTP_STATUS).\" >&2\n [[ -n \"$ERROR_CODE\" ]] && echo \" Code : $ERROR_CODE\" >&2\n [[ -n \"$ERROR_MSG\" ]] && echo \" Message: $ERROR_MSG\" >&2\n exit 1\nfi\n\nMESSAGE_ID=$(echo \"$RESPONSE\" | json_get \"id\")\n\necho \"Notification sent successfully.\"\n[[ -n \"$MESSAGE_ID\" ]] && echo \" Message ID: $MESSAGE_ID\"\n\nset_octopusvariable \"MessageId\" \"$MESSAGE_ID\""
},
"Category": "Discord",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates/discord-send-notification.json",
"Website": "/step-templates/5adb6692-04b1-4475-800b-fbf0652696ed",
"Logo": "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",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Monday, July 20, 2026