Skip to content

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.


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.


Click Add webhook on the Webhooks card (or a format tile when the list is empty). The sheet that opens has three steps.

Pick a format, give the webhook a name, and paste its URL. Each format shows where to find the URL in that tool.

FormatURL looks likeWhere to get it
Generic JSONAny http:// or https:// URLThe receiving system’s “incoming webhook” or HTTP trigger
Slackhttps://hooks.slack.com/services/…Slack Apps → Incoming Webhooks → Add to Slack, then pick the channel
Microsoft TeamsA Power Automate / Logic Apps URLChannel ⋯ → Workflows → Post to a channel when a webhook request is received
Discordhttps://discord.com/api/webhooks/…Server Settings → Integrations → Webhooks → New Webhook, then Copy Webhook URL
Google Chathttps://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.

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:

OptionSends
EverythingInfo, warning and error events
Warnings and errorsSkips routine info events such as task completions
Errors onlyFailures 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 Authorization header. 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.

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.


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.

EventCategorySeveritySent when
config.changedconfigwarningA downloaded configuration differs from the previous version
config.download_completedconfiginfoA manual config download finishes
config.purge_completedconfiginfoA configuration purge finishes
config.purge_failed_completedconfiginfoA failed-config purge finishes
snippet.sentconfiginfoA config snippet is sent to devices
config.snmp_trapconnectionwarningAn SNMP trap matches a filter with an alert action
connection.device_failureconnectionerrorrConfig cannot connect to a device (SSH, Telnet, API, TL1 or agent)
import.bulk_completedimportinfoA bulk device import succeeds
import.bulk_failedimporterrorA bulk device import fails
task.completedtaskinfoA scheduled task completes
task.download_reporttaskinfoA download task report is ready
compliance.reportcomplianceinfoA policy compliance report finishes
compliance.device_failedcomplianceerrorA device starts failing a policy assignment
compliance.device_not_evaluatedcomplianceerrorA device can no longer be checked against a policy assignment
system.auth_changedauthenticationerrorLDAP or RADIUS authentication settings change
system.notification_errorsystemerrorA notification fails, or a webhook is switched off after repeated failures

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 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 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.

"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.

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.

HeaderValue
Content-Typeapplication/json
User-AgentrConfig-Webhook/1
X-rConfig-EventThe event name, e.g. config.changed
X-rConfig-Event-IdSame as id in the body, unchanged across retries
X-rConfig-DeliveryUnique per attempt, matches the delivery ID in the log
X-rConfig-Signaturet=<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.


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 hashlib
import hmac
import 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"])

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.


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.

Terminal window
# Delivery errors are also logged here
tail -100 /var/www/html/rconfig8/current/storage/logs/laravel.log | grep -i webhook