Notifications · free in Webhook Actions

Know the moment a webhook fails.

Retries handle the flaky minute. This handles the endpoint that stays down at 3 a.m. — a Slack card, an email, a text or a page the moment a delivery gives up, and the all-clear when it recovers. Rules decide when and where. Every message is a template you can preview against a real payload, or let the AI draft.

  13 channels   6 delivery events   Quiet times & digests   Nothing Pro-gated
Where it tells you

Thirteen destinations. Secrets never shown again.

Email
wp_mail() — your SMTP plugin applies
Slack
Block Kit card via incoming webhook
Discord
embed via webhook URL
Telegram
bot token + chat or topic
Microsoft Teams
Workflows webhook (Adaptive Card)
Google Chat
space webhook
Mattermost
and Rocket.Chat
Pushover
phone push
ntfy
self-hosted or ntfy.sh
SMS
Twilio
WhatsApp
Twilio template
PagerDuty
Events v2 — opens, then resolves on recovery
Any URL
JSON + HMAC signature

A Slack URL or a bot token is stored encrypted in the same envelope the Credentials Vault uses and is write-only over the API — the channel card shows its last four characters, nothing more. Every channel has a Send test button, and one that keeps failing is called out in the plugin's health bar.

How it works

A channel, a rule, a message.

01 / Channel

Somewhere a message can go

An inbox, a Slack or Discord channel, a Telegram chat, a phone number, a PagerDuty service. Paste the URL or token once; the plugin keeps it encrypted. Click the paper plane to see a test land.

02 / Rule

When, and to whom

Pick the event, optionally narrow it — attempt 1 only, 5xx only, this trigger only — and pick the channels. Site-wide rules apply to every webhook; a rule can also belong to one webhook alone.

  • Attempt failed — One try failed. Filter by attempt number and HTTP code, or you will hear about every retry.
  • Retry scheduled — Same moment, plus when the next try is.
  • Permanently failed — The plugin has stopped trying — out of attempts, or a 4xx that will never succeed. This is the one to page on.
  • Recovered — Delivered after earlier failures. The all-clear.
  • Delivered — Every success. Pair it with a daily digest, never raw.
  • Skipped — Conditions did not pass. Usually expected.
03 / Message

What lands in Slack

A card with the webhook, the trigger, the attempt, the HTTP code, the error and a button straight to the delivery log. Email gets a formatted mail, SMS the one-liner, PagerDuty an incident. Same message, each channel's native shape.

Signal, not noise

Only the noise you asked for.

Filters
Only what matters
Attempt 1 only. 5xx and timeouts, not 404s. Out of attempts, not a bad request. Triggers matching woocommerce_*.
Quiet time
One message, not a hundred
At most one message per webhook in the window you set. The rest are recorded as throttled, not lost.
Digests
A summary, hourly or daily
Everything from the period folded into one message per channel — what failed, how often, per webhook.
Per webhook
Inherit, mute, or go custom
Every webhook inherits the site-wide rules. Mute one with a click, keep only its own rules, or switch it off.

Per webhook — inherit, mute one, or go custom

Filters — attempt, HTTP code, reason, trigger

Templates

Every message is a template. The AI can write it.

Subject, title, body and a one-liner for SMS use the same {{ path }} placeholders as dynamic URLs — over the webhook, the delivery, the mapped payload, the raw hook arguments and the site — with modifiers such as | truncate:120, | default:"—" and | date:"Y-m-d H:i". An Insert field picker lists every path in your captured payload with a sample value. A linter flags a path that is not there before you save. Draft with AI writes the first version from that payload.

Body template — WooCommerce order that failed to reach the CRM
Order {{ payload.order.id | default:"?" }} did not reach HubSpot.
Customer: {{ payload.billing.email }}
Attempt {{ delivery.attempt }} of {{ delivery.max_attempts }}
HTTP {{ delivery.http_code | default:"no response" }}
{{ delivery.error_message | truncate:160 }}

{{ delivery.log_url }}

Fields — start from the default, not a blank

Preview — rendered as you type

Field picker — every path, with a sample

Linter — a path the payload lacks, before you save

For developers

One hook for the whole lifecycle.

The dispatcher fires fswa_delivery_event at every state change with the full context, so your own code can react without querying anything back. fswa_notification_message rewrites a rendered message, fswa_notification_channel_drivers adds a channel type, and five abilities let Build with AI — or Claude Code over MCP — create and test rules.

add_action( 'fswa_delivery_event', function ( $event, $ctx ) {
    if ( $event !== 'permanently_failed' ) return;
    // $ctx: webhook, trigger, attempt, http_code, error_message,
    //       response_body, payload, original_payload, log_id …
    my_incident_tool()->open(
        $ctx['webhook']['name'],
        $ctx['error_message']
    );
}, 10, 2 );
Questions

Before you switch it on.

Is this a Pro feature? +
No. Every channel, rule, template, digest and the AI draft are in the free plugin. Nothing in Notifications is gated behind a licence.
Will a busy WooCommerce shop flood my inbox? +
Not if you use the controls built for that. A site-wide "every success" rule starts with a one-hour quiet time per webhook. Failure rules can be limited to the first attempt, to 5xx responses, or folded into an hourly or daily digest. The Sent tab shows what was throttled, so nothing disappears silently.
Does a notification slow down my site? +
No. The dispatcher records the event; the message is queued and sent on the next queue tick or a nudged cron run, never inside a visitor's request. A failed send retries up to three times on its own.
Where are the Slack URL and bot tokens stored? +
Encrypted at rest in the same envelope the Credentials Vault uses, and write-only over the REST API — a channel card shows only the last four characters. Define FSWA_SECRET_KEY in wp-config.php to keep the key out of the database.
Can I write the message myself? +
Every message is a template: subject, title, body and a one-liner for SMS, with {{ path }} placeholders over the webhook, the delivery (attempt, HTTP code, error, next try, log link), the mapped payload, the raw hook arguments and the site. An Insert field picker lists every path in your captured payload, a linter flags a path that is not there, and Draft with AI writes the first version for you.
Can PagerDuty resolve the incident when the webhook recovers? +
Yes. A Permanently failed rule pointed at a PagerDuty channel opens an incident keyed on the webhook, so repeated failures update one incident. A Recovered rule on the same channel resolves it.
Can I add my own channel type? +
Yes. The fswa_notification_channel_drivers filter registers a driver class with its own form fields and send method; secret fields are encrypted like the built-in ones. The fswa_notification_message filter changes any rendered message before it goes out.

The next failed delivery tells you itself.

Free plugin, five-minute setup, nothing to subscribe to.