How to Set Up a Discord Webhook in Claude Code

In this article

The fastest way to set up Discord webhook notifications in Claude Code is via Claude Code hooks — shell scripts that fire at lifecycle events like task completion, errors, or tool use. Create a Discord webhook URL in your server settings, then add a PostToolUse or Stop hook that sends a curl POST request to that URL. No plugins required. Works on any project, any OS.

  • Claude Code hooks support PreToolUse, PostToolUse, Notification, Stop, and SubagentStop events
  • Discord webhooks accept a simple JSON payload: {"content": "your message"}
  • Setup takes under 5 minutes and requires only a Discord server with "Manage Webhooks" permission

What are Claude Code hooks and why use them for Discord?

Claude Code hooks are user-defined shell commands that run automatically at specific points in the agent's lifecycle. They let you integrate Claude Code with external services without writing a plugin or modifying your project code. According to the Claude Code documentation, hooks execute in your shell environment with full access to environment variables and system tools like curl.

Pairing hooks with Discord webhooks means you can get a ping in your team server the moment Claude Code finishes a long-running task, hits an error, or completes a sub-agent job. This is especially useful when you step away from your machine and need to know when a complex PR build or migration script wraps up.

How to create a Discord webhook URL

Before touching Claude Code, you need a webhook URL from Discord:

  1. Open your Discord server and go to Server Settings (right-click the server icon).
  2. Navigate to Integrations and then Webhooks.
  3. Click New Webhook, give it a name (e.g., "Claude Code Alerts"), and choose the channel where notifications should post.
  4. Click Copy Webhook URL and save it somewhere safe. It looks like https://discord.com/api/webhooks/1234567890/xxxx...

Keep this URL private. Anyone with it can post to your channel. Store it as an environment variable rather than hard-coding it in config files.

How to configure the hook in Claude Code

Claude Code hooks are configured in your settings.json file (located at ~/.claude/settings.json for global hooks, or .claude/settings.json inside a project for project-scoped hooks).

Step 1: Store your webhook URL as an environment variable

Add the following to your shell profile (~/.zshrc, ~/.bashrc, etc.):

export DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_TOKEN"

Then reload your shell: source ~/.zshrc

Step 2: Add a Stop hook to settings.json

Open (or create) ~/.claude/settings.json and add a hooks block:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -H 'Content-Type: application/json' -d '{\"content\":\"Claude Code task finished!\"}' \"$DISCORD_WEBHOOK_URL\""
          }
        ]
      }
    ]
  }
}

The Stop event fires when Claude Code finishes a response turn. This is the most useful trigger for "task done" notifications.

Step 3: Test the webhook

Run any Claude Code task — even a simple one like /help. Check your Discord channel. You should see the "Claude Code task finished!" message appear within a second or two of Claude completing its response.

Sending richer Discord notifications with context

The basic example sends a static string. You can make notifications more useful by including dynamic context from Claude Code's hook environment. Hooks receive JSON on stdin describing the event, which you can pipe through jq to extract fields.

A more informative hook script (save as ~/.claude/discord-notify.sh):

#!/bin/bash
# Read the event JSON from stdin
INPUT=$(cat)
SESSION=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
MSG="Claude Code session \`$SESSION\` just finished a task."

curl -s \
  -H "Content-Type: application/json" \
  -d "{\"content\": \"$MSG\"}" \
  "$DISCORD_WEBHOOK_URL"

Make it executable: chmod +x ~/.claude/discord-notify.sh

Then reference the script in your settings:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/discord-notify.sh"
          }
        ]
      }
    ]
  }
}

Using Discord embed format for styled alerts

Discord webhooks support rich embeds. Replace the plain content payload with an embeds array for color-coded, structured messages:

curl -s -H 'Content-Type: application/json' \
  -d '{
    "embeds": [{
      "title": "Claude Code: Task Complete",
      "description": "Your agent finished running.",
      "color": 5763719
    }]
  }' "$DISCORD_WEBHOOK_URL"

The color field is a decimal integer (5763719 = green). Use red (15548997) for error hooks.

Which hook events should you use?

Claude Code exposes several hook events. The right one depends on what you want to monitor:

EventWhen it firesBest for
StopClaude finishes a response turn"Task done" pings
SubagentStopA sub-agent completesMulti-agent workflow alerts
PostToolUseAfter any tool (Bash, Write, etc.) runsMonitoring specific tool calls
NotificationClaude sends a notification eventForwarding Claude's own alerts
PreToolUseBefore a tool runsBlocking or logging tool calls

For most "notify my team when the agent is done" use cases, Stop is sufficient. If you're running long agentic pipelines with complex hook setups, consider SubagentStop to get per-agent granularity.

Scoping hooks to specific projects

Global hooks in ~/.claude/settings.json fire for every Claude Code session on your machine. To scope a Discord webhook to one project only, place the settings file at <project-root>/.claude/settings.json instead. This is useful when different projects should post to different Discord channels.

You can also use the matcher field to filter PostToolUse hooks to specific tools. For example, a matcher of "Bash" fires only when Claude runs a bash command — handy for alerting your team to any shell execution during a sensitive migration. See the full slash commands and hooks guide for matcher syntax details.

Staying aware of usage limits while running automated hooks

When you start wiring up hooks and leaving Claude Code running unattended, you can burn through your usage window faster than expected. Claude Code usage operates on a 5-hour rolling window — and hitting the limit mid-task means a hard stop until the window resets. You can check your current usage at any time with the /usage command inside Claude Code, or by visiting claude.ai/settings/usage.

For a passive, always-visible signal, Usagebar sits in your macOS menu bar and shows your Claude Code usage at a glance. It sends proactive alerts at 50%, 75%, and 90% of your limit, so you know well before you hit a wall. It uses macOS Keychain for secure credential storage and is available on a pay-what-you-want basis, including free for students. When you're relying on Discord webhooks to tell you when a job finishes, the last thing you want is to discover the job stalled because your usage limit ran out mid-task.

Get Usagebar and stop getting surprised by usage resets.

Troubleshooting common issues

  • No message appears in Discord: Check that $DISCORD_WEBHOOK_URL is exported in the same shell environment Claude Code runs in. Run echo $DISCORD_WEBHOOK_URL in the terminal where you launch Claude Code.
  • curl: command not found: Install curl (brew install curl on macOS) or use an alternative like wget or a small Node.js script.
  • Hook runs but Discord returns a 400 error: Your JSON payload likely has a quoting issue. Test the curl command directly in your terminal before adding it to settings.
  • Hook fires too frequently: Switch from PostToolUse (fires on every tool call) to Stop (fires once per response turn) to reduce noise.
  • Webhook URL expired or invalid: Regenerate the webhook in Discord's Integrations settings and update your environment variable.

Key takeaways

  1. Create a Discord webhook URL in your server's Integrations settings and store it as an environment variable.
  2. Add a Stop hook in ~/.claude/settings.json with a curl command targeting your webhook URL.
  3. Use a shell script for richer notifications that include session context via jq.
  4. Scope hooks to individual projects using a project-level .claude/settings.json file.
  5. Monitor your usage limits passively with Usagebar so automated tasks don't get cut off mid-run.

Sources

Never Get Locked Out Mid-Task Again

Never hit your usage limits unexpectedly. Usagebar lives in your menu bar and shows your 5-hour and weekly limits at a glance.

Get Usagebar

$9 — one-time, lifetime updates