Skip to main content
All articles
· Updated February 15, 2026

Send Discord Webhooks Using curl (Command Line Guide)

Complete guide to sending Discord webhook messages with curl. Includes examples for text, embeds, files, and advanced formatting from the command line.

curlcommand linewebhooktutorialcurl discord 2026command line webhookbash discord
Send Discord Webhooks Using curl (Command Line Guide)

Why Use curl for Discord Webhooks?

curl is a command-line tool available on virtually every platform (Linux, macOS, Windows). It’s perfect for:

  • Quick testing: Send test messages without writing code
  • Shell scripts: Integrate Discord notifications into bash/zsh scripts
  • CI/CD pipelines: Send build notifications from Jenkins, GitLab CI, or custom automation
  • Debugging: Test webhook payloads before implementing them in applications
  • Cron jobs: Schedule automated status updates or reports

This guide covers practical curl examples for Discord webhooks, from simple text messages to complex embeds.

Prerequisites

Check if curl is installed:

curl --version

If not installed:

  • Linux: sudo apt install curl or sudo yum install curl
  • macOS: Included by default
  • Windows: Included in Windows 10+ or download from curl.se

You’ll also need a Discord webhook URL from Server Settings → Integrations → Webhooks.

Basic Text Message

Send a simple text message:

curl -H "Content-Type: application/json" \
     -d '{"content": "Hello from curl!"}' \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

Breakdown:

  • -H "Content-Type: application/json": Sets the content type header
  • -d '{"content": "..."}': Sends JSON data in the request body
  • The webhook URL is the final argument

A successful request returns HTTP 204 with no response body.

Send with Custom Username and Avatar

Override the webhook’s default name and avatar:

curl -H "Content-Type: application/json" \
     -d '{
       "content": "Deployment complete!",
       "username": "Deploy Bot",
       "avatar_url": "https://example.com/bot-avatar.png"
     }' \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

The username and avatar_url fields are optional and apply only to this message.

Send a Rich Embed

Embeds provide formatted messages with colors, fields, and structure:

curl -H "Content-Type: application/json" \
     -d '{
       "embeds": [{
         "title": "Server Status",
         "description": "All systems operational",
         "color": 5763719,
         "fields": [
           {
             "name": "CPU Usage",
             "value": "23%",
             "inline": true
           },
           {
             "name": "Memory",
             "value": "4.2 GB / 16 GB",
             "inline": true
           }
         ],
         "footer": {
           "text": "Monitoring System"
         },
         "timestamp": "2025-04-05T14:30:00.000Z"
       }]
     }' \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

Color values are decimal integers:

  • Green: 5763719
  • Red: 15548997
  • Blue: 3447003
  • Discord Blurple: 5793266

Convert hex to decimal: echo $((16#5865F2)) outputs 5793266.

Using Variables in Shell Scripts

Store the webhook URL and create reusable functions:

#!/bin/bash

WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"

send_message() {
  local message="$1"
  curl -H "Content-Type: application/json" \
       -d "{\"content\": \"$message\"}" \
       "$WEBHOOK_URL"
}

send_embed() {
  local title="$1"
  local description="$2"
  local color="$3"
  
  curl -H "Content-Type: application/json" \
       -d "{
         \"embeds\": [{
           \"title\": \"$title\",
           \"description\": \"$description\",
           \"color\": $color
         }]
       }" \
       "$WEBHOOK_URL"
}

# Usage
send_message "Backup started at $(date)"
send_embed "Backup Complete" "All files backed up successfully" 5763719

Important: Escape double quotes inside JSON strings when using variables.

Send Files and Attachments

To send files, use multipart/form-data:

curl -F "payload_json={\"content\": \"Here's the log file:\"}" \
     -F "file=@/path/to/server.log" \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

Breakdown:

  • -F: Sends form data (multipart/form-data)
  • payload_json: The message payload as JSON
  • file=@/path/to/file: Uploads the file (@ prefix reads from file path)

Send multiple files:

curl -F "payload_json={\"content\": \"Multiple files:\"}" \
     -F "file1=@/path/to/file1.txt" \
     -F "file2=@/path/to/file2.log" \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

Embed with Image and Thumbnail

Add images to embeds:

curl -H "Content-Type: application/json" \
     -d '{
       "embeds": [{
         "title": "Server Screenshot",
         "description": "Current dashboard view",
         "color": 5793266,
         "image": {
           "url": "https://example.com/screenshot.png"
         },
         "thumbnail": {
           "url": "https://example.com/icon.png"
         }
       }]
     }' \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

Image URLs must be publicly accessible (HTTPS recommended).

Error Handling in Scripts

Check the HTTP response code and handle errors:

#!/bin/bash

WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"

send_with_retry() {
  local message="$1"
  local max_retries=3
  local retry_count=0

  while [ $retry_count -lt $max_retries ]; do
    response=$(curl -s -w "%{http_code}" -o /dev/null \
                    -H "Content-Type: application/json" \
                    -d "{\"content\": \"$message\"}" \
                    "$WEBHOOK_URL")

    if [ "$response" -eq 204 ]; then
      echo "Message sent successfully"
      return 0
    elif [ "$response" -eq 429 ]; then
      echo "Rate limited. Retrying in 2 seconds..."
      sleep 2
      retry_count=$((retry_count + 1))
    else
      echo "Error: HTTP $response"
      return 1
    fi
  done

  echo "Failed after $max_retries retries"
  return 1
}

send_with_retry "Test message with retry logic"

Common HTTP codes:

  • 204: Success
  • 400: Bad request (invalid JSON)
  • 401: Unauthorized (invalid webhook URL)
  • 429: Rate limited (too many requests)

Real-World Example: Backup Script Notifications

Send Discord notifications from a backup script:

#!/bin/bash

WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"
BACKUP_DIR="/var/backups"
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")

# Start notification
curl -H "Content-Type: application/json" \
     -d "{\"content\": \"🔄 Backup started at $TIMESTAMP\"}" \
     "$WEBHOOK_URL"

# Perform backup
tar -czf "$BACKUP_DIR/backup-$(date +%Y%m%d).tar.gz" /home/user/data

if [ $? -eq 0 ]; then
  # Success
  BACKUP_SIZE=$(du -h "$BACKUP_DIR/backup-$(date +%Y%m%d).tar.gz" | cut -f1)
  
  curl -H "Content-Type: application/json" \
       -d "{
         \"embeds\": [{
           \"title\": \"✅ Backup Complete\",
           \"description\": \"Backup finished successfully\",
           \"color\": 5763719,
           \"fields\": [
             {\"name\": \"Size\", \"value\": \"$BACKUP_SIZE\", \"inline\": true},
             {\"name\": \"Time\", \"value\": \"$TIMESTAMP\", \"inline\": true}
           ]
         }]
       }" \
       "$WEBHOOK_URL"
else
  # Failure
  curl -H "Content-Type: application/json" \
       -d "{
         \"embeds\": [{
           \"title\": \"❌ Backup Failed\",
           \"description\": \"Backup process encountered an error\",
           \"color\": 15548997,
           \"fields\": [
             {\"name\": \"Time\", \"value\": \"$TIMESTAMP\", \"inline\": false}
           ]
         }]
       }" \
       "$WEBHOOK_URL"
fi

Real-World Example: Server Monitoring

Monitor server resources and send alerts:

#!/bin/bash

WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"

# Get system stats
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
MEMORY_USAGE=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100}')
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1)

# Determine alert level
if [ $(echo "$CPU_USAGE > 80" | bc) -eq 1 ] || \
   [ $(echo "$MEMORY_USAGE > 80" | bc) -eq 1 ] || \
   [ $DISK_USAGE -gt 80 ]; then
  COLOR=15548997  # Red
  TITLE="⚠️ High Resource Usage Alert"
else
  COLOR=5763719   # Green
  TITLE="✅ System Status Normal"
fi

curl -H "Content-Type: application/json" \
     -d "{
       \"embeds\": [{
         \"title\": \"$TITLE\",
         \"color\": $COLOR,
         \"fields\": [
           {\"name\": \"CPU Usage\", \"value\": \"${CPU_USAGE}%\", \"inline\": true},
           {\"name\": \"Memory Usage\", \"value\": \"${MEMORY_USAGE}%\", \"inline\": true},
           {\"name\": \"Disk Usage\", \"value\": \"${DISK_USAGE}%\", \"inline\": true}
         ],
         \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\"
       }]
     }" \
     "$WEBHOOK_URL"

Run this script via cron for periodic monitoring:

# Check every 5 minutes
*/5 * * * * /path/to/monitor.sh

Using jq for Complex JSON

For complex JSON payloads, use jq to build JSON programmatically:

# Install jq: sudo apt install jq

WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"

# Build JSON with jq
PAYLOAD=$(jq -n \
  --arg title "Deployment Report" \
  --arg desc "Version 2.1.0 deployed" \
  --argjson color 5763719 \
  '{
    embeds: [{
      title: $title,
      description: $desc,
      color: $color,
      timestamp: (now | strftime("%Y-%m-%dT%H:%M:%S.000Z"))
    }]
  }')

curl -H "Content-Type: application/json" \
     -d "$PAYLOAD" \
     "$WEBHOOK_URL"

jq handles escaping and formatting automatically, reducing errors.

Rate Limits

Discord enforces rate limits:

  • 5 requests per 2 seconds per webhook
  • 30 requests per 60 seconds per channel

If you exceed limits, you’ll receive HTTP 429. Add delays between requests:

for i in {1..10}; do
  curl -H "Content-Type: application/json" \
       -d "{\"content\": \"Message $i\"}" \
       "$WEBHOOK_URL"
  sleep 0.5  # 500ms delay
done

Debugging curl Requests

Use verbose mode to see full request/response details:

curl -v -H "Content-Type: application/json" \
     -d '{"content": "Debug test"}' \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

The -v flag shows:

  • Request headers
  • Response headers
  • HTTP status code
  • SSL/TLS handshake details

Best Practices

Store webhook URLs securely: Use environment variables or config files with restricted permissions. Never commit webhook URLs to version control.

# .env file (add to .gitignore)
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."

# Load in script
source .env

Validate JSON: Use jq to validate JSON before sending:

echo '{"content": "test"}' | jq .

Handle special characters: Escape quotes and newlines in shell variables:

MESSAGE="Line 1\nLine 2"
curl -d "{\"content\": \"$MESSAGE\"}" "$WEBHOOK_URL"

Test payloads visually: Use the Discord Webhook Builder to design embeds, then copy the JSON for use in curl commands.

Troubleshooting

400 Bad Request: Invalid JSON syntax. Validate with jq or an online JSON validator.

401 Unauthorized: Webhook URL is incorrect or the webhook was deleted.

curl: command not found: Install curl using your package manager.

SSL certificate errors: Update curl or use -k to skip verification (not recommended for production).

Next Steps

You now know how to send Discord webhooks using curl from the command line. For complex embed designs, try our free Discord Webhook Builder to visually create messages, then export the JSON for use in your shell scripts and automation workflows.

discord-webhook.com also offers scheduled messages, thread and forum support, polls, and interactive buttons with actions — all configurable through the visual builder without writing code.