I recently set up Alfred — my personal coding assistant — and needed a reliable way to know exactly when it's ready to work. Unlike always-on services, Alfred runs on-demand: I boot it via Wake-on-LAN when I need it and shut it down when I'm done. To receive a notification when the gateway becomes operational, I implemented an OpenClaw hook.
This article explains step by step how to create a hook that sends a Discord notification at gateway startup. Whether you want to track availability, monitor boot performance, or simply get a ping when your assistant is ready, this guide will get you there.
Prerequisites
Before we begin, make sure you have:
- OpenClaw installed and configured on your machine
- Administrative access to the machine where the gateway runs
- A Discord account and a channel or DM where you want to receive notifications
- Basic familiarity with JavaScript/Node.js for the handler implementation
You'll also need to decide whether you want to use OpenClaw's built-in Discord integration
(which uses openclaw message send) or connect directly via a Discord webhook.
Hook anatomy
Every OpenClaw hook consists of two parts: a manifest (HOOK.md) that declares what the hook does and when it fires,
and a handler (typically handler.js) that implements the actual logic.
The manifest is a YAML front-matter document that specifies the hook's metadata. Here's a minimal example:
---
name: discord-notify
description: "Sends a Discord DM when the gateway boots"
metadata: {
"openclaw": {
"emoji": "🚀",
"events": ["gateway:startup"]
}
}
---
Key fields:
name: unique identifier for the hookdescription: brief summary of its purposemetadata.openclaw.emoji: an emoji to display in hook listingsmetadata.openclaw.events: array of events that trigger this hook (in this case,gateway:startup)
Available events include gateway:startup, gateway:shutdown, agent:spawn, and more.
For our use case, gateway:startup is exactly what we need.
Handler implementation
The handler is a JavaScript module that exports an async function.
OpenClaw passes an event object containing contextual information about what triggered the hook.
Here's a complete example that sends a message via OpenClaw's native Discord integration:
import { execSync } from 'child_process';
const handler = async (event) => {
if (event.type !== "gateway" || event.action !== "startup") return;
// Extract useful information from the context
const version = event.context?.cfg?.meta?.lastTouchedVersion || "Unknown";
const aiModel = event.context?.cfg?.agents?.defaults?.model?.primary || "Default model";
const bootTime = new Date().toLocaleTimeString('en-US');
const message =
`🚀 **Alfred is online!** | 🤖 Model: ${aiModel} | 📦 Version: ${version} | ⏰ ${bootTime}`;
console.log(`[discord-notify] Sending Discord report (${aiModel} - v${version})...`);
try {
// Replace with your target channel or DM ID
execSync(
`openclaw message send --channel discord --target "YOUR_CHANNEL_ID" --message "${message}"`
);
} catch (error) {
console.error("Discord notification failed:", error.message);
}
};
export default handler;
Notes on this approach:
- We check the
event.typeandevent.actionto ensure we're responding to the right event. - Information about the AI model, version, and boot time is extracted from the event context.
- The message is formatted with Markdown and sent via the
openclaw message sendcommand.
Alternative approach: Instead of using OpenClaw's Discord integration, you can send directly via a Discord webhook:
import { execSync } from 'child_process';
import { readFile } from 'fs/promises';
const handler = async (event) => {
if (event.type !== "gateway" || event.action !== "startup") return;
const version = event.context?.cfg?.meta?.lastTouchedVersion || "Unknown";
const aiModel = event.context?.cfg?.agents?.defaults?.model?.primary || "Default model";
const bootTime = new Date().toLocaleTimeString('en-US');
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
if (!webhookUrl) {
console.error("DISCORD_WEBHOOK_URL environment variable not set");
return;
}
const message = {
content: `🚀 **Alfred is online!** | 🤖 Model: ${aiModel} | 📦 Version: ${version} | ⏰ ${bootTime}`
};
console.log(`[discord-notify] Sending webhook notification...`);
try {
execSync(
`curl -X POST -H "Content-Type: application/json" -d '${JSON.stringify(message)}' "${webhookUrl}"`,
{ stdio: 'inherit' }
);
} catch (error) {
console.error("Webhook notification failed:", error.message);
}
};
export default handler;
This variant uses environment variables to securely store your webhook URL and makes a direct HTTP POST to Discord's API.
Install & enable
Once your hook is ready, follow these steps to install and test it:
- Create the hook directory — each hook lives in its own folder:
mkdir -p ~/.openclaw/hooks/discord-notify - Add the files — place
HOOK.mdandhandler.jsin the new directory:~/.openclaw/hooks/discord-notify/ ├── HOOK.md └── handler.js - Enable the hook:
openclaw hooks enable discord-notify - Verify installation:
openclaw hooks list - Test it by restarting the gateway:
openclaw gateway restart
After the gateway restarts, you should receive a Discord notification with the model name, version, and boot time.
Security considerations
When implementing hooks, keep these security best practices in mind:
- Never store credentials in plain text. Use environment variables or OpenClaw's secure configuration system.
- Be cautious with shell commands. Hooks that execute external commands may trigger approval requirements depending on your security policy.
- Validate event data. Don't blindly trust information from the event object, especially if you're using it in shell commands.
- Use the principle of least privilege. Only request permissions that are absolutely necessary for your hook to function.
For webhook-based notifications, storing the webhook URL in an environment variable is safer than hardcoding it in your handler script.
Variants & troubleshooting
Once you have the basic hook working, you can extend it in various ways:
- Different destinations: Send to a public channel instead of a DM, or create multiple hooks for different purposes.
- Enriched notifications: Add more details like local IP address, system uptime, or a changelog of the current version.
- Other events: Trigger on
agent:spawn,session:ready, or custom events specific to your use case. - Error handling: Implement retry logic with exponential backoff for transient failures.
- Logging: Add structured logging for debugging and monitoring hook behavior in production.
Troubleshooting tips:
- If you don't receive a notification, check the hook logs with
openclaw hooks logs discord-notify. - Verify that your Discord credentials or webhook URL are correctly configured.
- Ensure the gateway has network access to Discord's API (especially for webhook-based approaches).
- Test your handler locally by simulating the event if debugging is needed.
Conclusion
OpenClaw hooks provide a powerful and straightforward way to automate tasks at key moments in your assistant's lifecycle. For my use case with Alfred, they give me a reliable signal when the gateway is ready — and open the door to automating post-boot tasks like health checks, context reloading, or monitoring tasks.
The combination of a simple manifest-based interface and JavaScript handlers makes it easy to customize notifications, integrations, and workflows to your exact needs. Whether you're tracking availability, monitoring boot performance, or just getting a friendly ping when your assistant comes online, hooks are the right tool for the job.
For more information, check out the official OpenClaw hooks documentation.