Skip to content

feat(alerts): add OpenClaw webhook delivery support#300

Open
0xlaveen wants to merge 2 commits intomainfrom
feature/openclaw-webhook-delivery
Open

feat(alerts): add OpenClaw webhook delivery support#300
0xlaveen wants to merge 2 commits intomainfrom
feature/openclaw-webhook-delivery

Conversation

@0xlaveen
Copy link
Contributor

@0xlaveen 0xlaveen commented Mar 14, 2026

Summary

Adds provider-agnostic webhook delivery for Smart Alerts. Supports multiple webhook targets out of the box.

Providers

  • openclaw — POST to /hooks/agent with agent payload format
  • generic — simple JSON POST with raw alert data + configurable headers
  • slack — Slack incoming webhook with blocks formatting
  • discord — Discord webhook with rich embeds

Usage

# Generic webhook
nansen alerts create --webhook https://example.com/hook --webhook-type generic ...

# OpenClaw (original)
nansen alerts create --openclaw https://gw.openclaw.ai/hooks/agent ...

# Slack
nansen alerts create --webhook https://hooks.slack.com/... --webhook-type slack ...

# Relay mode
nansen alerts webhook-relay --url https://example.com --type discord --once
nansen alerts openclaw-relay --url https://gw.openclaw.ai/hooks/agent  # backward compat alias

Env vars

WEBHOOK_RELAY_URL / WEBHOOK_RELAY_TOKEN (falls back to OPENCLAW_HOOKS_URL / OPENCLAW_HOOKS_TOKEN)

Changes

  • src/webhook.js — Provider registry, formatters for all 4 providers, delivery with retry, relay poller
  • src/commands/alerts.js--webhook/--webhook-type flags, webhook-relay subcommand, backward compat aliases
  • src/schema.json — Updated schema
  • src/__tests__/webhook.test.js — Tests for all providers, backward compat, relay modes

All 1069 tests pass.

Add --openclaw channel flag for alert create/update and a new
`alerts openclaw-relay` subcommand that polls for alerts and forwards
them to OpenClaw's /hooks/agent endpoint with exponential backoff retry.

Supports one-shot (--once) and long-running polling modes, with
filtering by alert type or ID, and configurable OpenClaw agent options.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nansen-pr-reviewer
Copy link

nansen-pr-reviewer bot commented Mar 14, 2026

pr-reviewer Summary

📝 3 findings

Review completed. Please address the findings below.

Findings by Severity

Severity Count
🟡 Medium 2
🔵 Low 1

Review effort: 3/5 (Moderate)

Summary

This PR adds a clean multi-provider webhook delivery system (Slack, Discord, generic, OpenClaw) with full backward compatibility for the previous openclaw-relay command. The architecture is solid, retry logic is well-implemented, and test coverage is thorough. Three issues worth addressing are noted below.

Findings

src/webhook.jsPROVIDERS[x].buildHeaders is dead code

Severity: medium

Every entry in the PROVIDERS registry defines a buildHeaders function, but it is never called anywhere. deliverWebhook builds its own headers internally (lines 235–239), completely bypassing the provider's buildHeaders:

// webhook.js – deliverWebhook always builds headers itself
const headers = {
  'Content-Type': 'application/json',
  ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
  ...headerOverrides,
};

The immediate consequence is that for slack and discord providers, deliverWebhook will append Authorization: Bearer <token> whenever --token is provided. Both services use URL-embedded authentication; Slack and Discord silently ignore unknown headers, so delivery still works — but the token is sent unnecessarily to a third-party endpoint, and the provider-specific intent (no auth header for Slack/Discord) is not honoured.

Suggested fix: Either have deliverWebhook accept the provider and delegate to provider.buildHeaders(token, headerOverrides) for header construction, or remove buildHeaders from the PROVIDERS registry and document that token-based auth is always Bearer.


src/commands/alerts.js line 566 — stale error message omits --webhook

Severity: medium

When alerts create is called without a notification channel, the error message reads:

missing.push('a channel (--telegram, --slack, --discord, or --openclaw)');

The new --webhook <url> flag (the primary new delivery option added in this PR) is absent from the list. Users who read the error won't know --webhook is an option.

Suggested fix:

missing.push('a channel (--telegram, --slack, --discord, --webhook <url>, or --openclaw)');

src/webhook.jsseenAlerts Set grows without bound in relayAlerts

Severity: low

In relayAlerts, the seenAlerts Set (line 329) accumulates one entry per unique alertId:data combination and is never pruned. For long-running relay processes where alert data changes frequently, this Set grows indefinitely. For typical production use (small number of alerts polled over days/weeks) the impact is negligible, but it is worth noting.

Suggested fix: Cap the Set size or use an LRU cache if long-running processes are an expected use case.


Token usage: 3,913 input, 6,853 output, 245,387 cache read, 40,149 cache write

Refactors OpenClaw-specific webhook into a multi-provider system:
- openclaw: /hooks/agent format (original behavior)
- generic: simple JSON POST with raw alert data
- slack: Slack incoming webhook with blocks
- discord: Discord webhook with embeds

New flags: --webhook <url> --webhook-type <type>
Backward compat: --openclaw still works as alias.
webhook-relay replaces openclaw-relay (alias preserved).

All 1069 tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant