Webhook Notifications
Webhook Notifications
Section titled “Webhook Notifications”Webhooks post rConfig events to another system the moment they happen. Point one at a Slack or Teams channel, a Discord or Google Chat space, or your own automation (ServiceNow, PagerDuty, n8n, a Python script), and choose exactly which events it receives.
A core switch starts failing its hardening policy at 02:00? The #netops-oncall channel gets a message naming the device and the failed rules, while the #change-log channel only ever sees configuration changes.
Best for: Routing alerts to the team that owns them, feeding ticketing or automation tools, and posting to more than one chat channel.
Where to find it
Section titled “Where to find it”Go to Settings → System Management → Notifications. The page shows a status card for each channel (Email, Slack, Teams and Webhooks), your webhook list, and the server-configured Slack and Teams integrations underneath.
Viewing webhooks needs permission to view settings. Adding, changing, testing or deleting them needs permission to update settings.
Add a webhook
Section titled “Add a webhook”Click Add webhook on the Webhooks card (or a format tile when the list is empty). The sheet that opens has three steps.
1. Where to send
Section titled “1. Where to send”Pick a format, give the webhook a name, and paste its URL. Each format shows where to find the URL in that tool.
| Format | URL looks like | Where to get it |
|---|---|---|
| Generic JSON | Any http:// or https:// URL | The receiving system’s “incoming webhook” or HTTP trigger |
| Slack | https://hooks.slack.com/services/… | Slack Apps → Incoming Webhooks → Add to Slack, then pick the channel |
| Microsoft Teams | A Power Automate / Logic Apps URL | Channel ⋯ → Workflows → Post to a channel when a webhook request is received |
| Discord | https://discord.com/api/webhooks/… | Server Settings → Integrations → Webhooks → New Webhook, then Copy Webhook URL |
| Google Chat | https://chat.googleapis.com/v1/spaces/… | Open the space → Apps & integrations → Webhooks → Add webhook |
Want the same events in two Slack channels? Add two Slack webhooks. There is no limit on how many webhooks you create.
2. What to send
Section titled “2. What to send”Turn on All events, including new ones to receive everything, including event types added in later rConfig versions. Otherwise, tick categories or individual events. The header shows how many event types the webhook will actually receive once the severity filter is applied.
Minimum severity cuts noise without unticking events:
| Option | Sends |
|---|---|
| Everything | Info, warning and error events |
| Warnings and errors | Skips routine info events such as task completions |
| Errors only | Failures that need attention, such as device failures |
3. Security and advanced (Generic JSON only)
Section titled “3. Security and advanced (Generic JSON only)”This step is collapsed by default and only appears for Generic JSON, because chat tools ignore signatures and custom headers.
- Signing secret: leave it blank and rConfig generates one when you save. You can paste your own (16 characters or more) if the receiver already has one.
- Custom headers: for receivers that need an API key or
Authorizationheader. Saved values are never shown again. Leave a value blank when editing to keep it. - Timeout: 1 to 30 seconds, default 10.
- Verify SSL: on by default. Turn it off only for internal receivers with self-signed certificates.
Test before you save
Section titled “Test before you save”Send test posts a webhook.test event using the values in the form, before anything is saved, and shows the receiver’s HTTP status and response inline. Preview payload shows the exact body the chosen format sends.
When you create a Generic JSON webhook, rConfig shows the signing secret once. Copy it into the receiving system now. You can rotate it later from the webhook’s ⋯ menu, which also shows the new secret once.
Events
Section titled “Events”Every webhook can subscribe to any of these events. The category is how they are grouped in the event picker, and the event and category values in the JSON payload.
| Event | Category | Severity | Sent when |
|---|---|---|---|
config.changed | config | warning | A downloaded configuration differs from the previous version |
config.download_completed | config | info | A manual config download finishes |
config.purge_completed | config | info | A configuration purge finishes |
config.purge_failed_completed | config | info | A failed-config purge finishes |
snippet.sent | config | info | A config snippet is sent to devices |
config.snmp_trap | connection | warning | An SNMP trap matches a filter with an alert action |
connection.device_failure | connection | error | rConfig cannot connect to a device (SSH, Telnet, API, TL1 or agent) |
import.bulk_completed | import | info | A bulk device import succeeds |
import.bulk_failed | import | error | A bulk device import fails |
task.completed | task | info | A scheduled task completes |
task.download_report | task | info | A download task report is ready |
compliance.report | compliance | info | A policy compliance report finishes |
compliance.device_failed | compliance | error | A device starts failing a policy assignment |
compliance.device_not_evaluated | compliance | error | A device can no longer be checked against a policy assignment |
system.auth_changed | authentication | error | LDAP or RADIUS authentication settings change |
system.notification_error | system | error | A notification fails, or a webhook is switched off after repeated failures |
Device compliance events
Section titled “Device compliance events”compliance.device_failed and compliance.device_not_evaluated fire per device, so they only fire when a device’s state changes. A device that fails at 02:00 sends one event. If it is still failing on the next ten scheduled runs, nothing more is sent until it passes and then fails again.
“Not evaluated” usually means there is no downloaded config for the command the policy checks. These two events are webhook only, so they don’t appear in users’ email or in-app preferences.
Deliveries, retries and failures
Section titled “Deliveries, retries and failures”Deliveries are queued, so the Horizon workers must be running. Each delivery is tried up to four times: once straight away, then after 30 seconds, 2 minutes and 10 minutes. Only a 2xx response counts as success. Redirects are not followed.
Open Deliveries from a webhook’s ⋯ menu to see every attempt from the last 30 days, with the status, duration, request body and response. Redeliver sends the same event again with the same event ID.
After 10 failed attempts in a row, rConfig switches the webhook off, marks it Switched off in the list, and raises a system.notification_error so administrators (and any other webhook watching system errors) hear about it. Fix the receiver, then use Re-enable in the Deliveries sheet or the list toggle. Re-enabling resets the failure count.
Generic JSON payload
Section titled “Generic JSON payload”Generic JSON webhooks receive the same envelope for every event. data holds the event-specific details.
{ "schema_version": 1, "id": "6f1c2a7e-3b8d-4a51-9f0e-2c7d5b9e8a14", "event": "config.changed", "category": "config", "severity": "warning", "occurred_at": "2026-10-24T02:00:14+00:00", "title": "Configuration changed on core-sw-01", "summary": "A configuration change was detected on core-sw-01 for command 'show running-config'.", "url": "https://rconfig.example.com/configs/view/630?ref=device-view", "instance": { "name": "rConfig", "url": "https://rconfig.example.com", "version": "8.4.1" }, "data": { "device": { "id": 12, "name": "core-sw-01", "category": "Switches" }, "config": { "id": 630, "command": "show running-config", "version": 4 }, "diff": { "lines": [ { "type": " ", "line": "41", "text": "interface Gi1/0/1" }, { "type": "-", "line": "42", "text": " description uplink-old" }, { "type": "+", "line": "42", "text": " description uplink-core-02" } ], "total_lines": 3, "truncated": false }, "diff_error": null }}id stays the same across retries and redeliveries, so receivers can use it to ignore duplicates.
Event data
Section titled “Event data”"data": { "device": { "id": 12, "name": "core-sw-01" }, "policy_assignment": { "id": 3, "name": "Core switches baseline" }, "policy_definition": { "id": 7, "name": "Baseline hardening" }, "command": "show running-config", "config_id": 630, "report_id": "4f8e2b1c-...", "result_id": 9811, "previous_state": "pass", "failed_rule_count": 2, "total_rule_count": 14, "failed_rules": [ { "method": "must_include", "description": "NTP servers configured" }, { "method": "must_not_include", "description": "Telnet disabled on VTY lines" } ]}failed_rules lists up to 25 rules. previous_state is pass, not_evaluated or null for a device’s first result. compliance.device_not_evaluated has the same fields without the rule counts, plus a reason.
"data": { "source_ip": "10.20.0.5", "filter": { "id": 4, "name": "Link down" }, "trap_oid": "1.3.6.1.6.3.1.1.5.3", "action": "send_alert", "trap": { "trap_oid": "1.3.6.1.6.3.1.1.5.3", "ifIndex": 4 }}"data": { "device": { "id": 12 }}The failure reason is in summary, and url links to the device.
config.changed diffs carry up to 200 lines. When there are more, truncated is true and total_lines gives the full count. If the compare itself failed, diff is null and diff_error explains why.
Headers
Section titled “Headers”| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | rConfig-Webhook/1 |
X-rConfig-Event | The event name, e.g. config.changed |
X-rConfig-Event-Id | Same as id in the body, unchanged across retries |
X-rConfig-Delivery | Unique per attempt, matches the delivery ID in the log |
X-rConfig-Signature | t=<unix timestamp>,v1=<hex HMAC-SHA256> when a secret is set |
Your custom headers are sent too, but they cannot override any of the headers above.
Verify the signature
Section titled “Verify the signature”The signature is an HMAC-SHA256 of <timestamp>.<raw request body> using the webhook’s signing secret. Recompute it from the raw body (not re-encoded JSON), compare in constant time, and reject timestamps older than five minutes to stop replays.
import hashlibimport hmacimport time
def verify(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool: parts = dict(pair.split("=", 1) for pair in header.split(",")) timestamp = int(parts["t"]) if abs(time.time() - timestamp) > tolerance: return False expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts["v1"])const crypto = require("crypto");
function verify(secret, header, rawBody, toleranceSeconds = 300) { const parts = Object.fromEntries(header.split(",").map((pair) => pair.split("=", 2))); const timestamp = Number(parts.t); if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false; const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex"); return parts.v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));}function verify(string $secret, string $header, string $body, int $tolerance = 300): bool{ parse_str(str_replace(',', '&', $header), $parts); $timestamp = (int) ($parts['t'] ?? 0); if (abs(time() - $timestamp) > $tolerance) { return false; } $expected = hash_hmac('sha256', $timestamp . '.' . $body, $secret);
return hash_equals($expected, $parts['v1'] ?? '');}Chat formats
Section titled “Chat formats”Slack, Teams, Discord and Google Chat webhooks receive a formatted message instead of the raw JSON: the title with a severity marker, the summary, key details such as the device and policy, failed compliance rules, and an Open in rConfig button. Configuration changes include a colour-coded diff, capped to fit each tool’s message limits.
The built-in Slack and MS Teams integrations configured in .env still work as before. Webhooks are the easier route when you need more than one channel or want to choose events per channel.
Troubleshooting
Section titled “Troubleshooting”Send test works but real events never arrive. Check that the Horizon workers are running and were restarted after the upgrade, and that the webhook subscribes to the event (and passes the minimum severity).
Test fails with “Receiver responded with HTTP 3xx”. The URL redirects, for example http:// to https://. Use the final URL; rConfig does not follow redirects.
Test fails with a certificate error. The receiver uses a self-signed or internal CA certificate. Add the CA to the rConfig server’s trust store, or turn off Verify SSL for that webhook.
Signature never matches. Verify against the raw request body before any JSON parsing, and check the receiver uses the current secret if it was rotated.
A webhook shows Switched off. It failed 10 times in a row. Open Deliveries to see the errors, fix the receiver, then re-enable it.
# Delivery errors are also logged heretail -100 /var/www/html/rconfig8/current/storage/logs/laravel.log | grep -i webhookRelated Documentation
Section titled “Related Documentation”- Compliance Overview for the policies behind the compliance events.
- SNMP Trap Administration for setting up trap filters with alert actions.