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.

From V8.4.1, webhooks are the recommended way to post to Slack and Microsoft Teams. The older .env-based Slack and MS Teams integrations are legacy: they still work, but only cover two event types and one channel each, and are scheduled for removal in a future major release. Each of those pages has a short section on moving to a webhook.


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 legacy server-configured Slack and Teams integrations underneath. When the legacy Slack or Teams integration isn’t set up, its card offers Add Slack webhook or Add Teams webhook instead.

Settings Notifications page with Email, Slack, Teams and Webhooks status cards above a webhook list showing name, format, events, minimum severity, last delivery and an enabled toggle
Settings Notifications page with Email, Slack, Teams and Webhooks status cards above a webhook list showing name, format, events, minimum severity, last delivery and an enabled toggle

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.

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.

Add webhook sheet, step 1, with the Generic JSON, Slack, Microsoft Teams, Discord and Google Chat format tiles, the Slack tile selected, and Name and Webhook URL fields filled in
Add webhook sheet, step 1, with the Generic JSON, Slack, Microsoft Teams, Discord and Google Chat format tiles, the Slack tile selected, and Name and Webhook URL fields filled in

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

Add webhook sheet, step 2, with the Configuration Changes and Compliance Reports categories expanded, two events ticked, Warnings and errors selected and the header reading Will receive 2 of 18 event types Add webhook sheet, step 2, with the Configuration Changes and Compliance Reports categories expanded, two events ticked, Warnings and errors selected and the header reading Will receive 2 of 18 event types

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.

Add webhook sheet, step 3, showing the signing secret field, one custom Authorization header row, the timeout field and the Verify SSL switch Add webhook sheet, step 3, showing the signing secret field, one custom Authorization header row, the timeout field and the Verify SSL switch

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 the category value in the JSON payload, and the event picker groups events by it. The two connection.agent_* events only appear when the Vector server is installed.

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)
connection.agent_down connection error A Vector agent misses too many check-ins and is marked down
connection.agent_recovered connection info A Vector agent that was marked down checks in again
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
system.health_check_failed system error One or more system health checks start failing
system.health_check_recovered system info One or more failing system health checks pass again

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.

connection.agent_down and connection.agent_recovered fire only when an agent’s status changes, so each outage sends one down event and one recovered event. The summary names the agent and its last check-in, and says how many devices were set to agent-down or restored.

From V8.4.1, system.health_check_failed and system.health_check_recovered also fire on a change only. rConfig runs its health checks every five minutes, and each run sends at most one failed event listing every check that started failing, and one recovered event for checks that passed again. Warnings, such as disk space above 70%, never fire. data.checks_affected names the checks, and data.checks lists each one with its name, label, status, summary and message.


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.

Webhook Deliveries sheet listing recent attempts with success and failure icons, HTTP status and duration, one attempt expanded to show the request body, response body and a Redeliver button Webhook Deliveries sheet listing recent attempts

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.

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.


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.

Slack channel showing an rConfig configuration change message with device details, a diff code block with added and removed lines, and an Open in rConfig button

Microsoft Teams channel showing an rConfig compliance failure Adaptive Card with device facts, a failed rules list and an Open in rConfig button

Diffs use the same colour-coded rendering as the legacy Slack and MS Teams integrations, so moving to a webhook keeps the detail your channel is used to. Switch the legacy integration off in .env once the webhook works, or each event posts twice.


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