---
title: "fswa_delivery_event — Action Hook"
description: "Fires at every delivery state change — success, recovered, failed attempt, retry scheduled, permanently failed, skipped — with the full context in hand."
url: "https://wpwebhooks.org/docs/fswa-delivery-event/"
---

[WP Webhooks](https://wpwebhooks.org/) / [Docs](https://wpwebhooks.org/docs/) / fswa\_delivery\_event

Action since v3.3.0

# fswa\_delivery\_event

Fires at every delivery state change — success, recovered, failed attempt, retry scheduled, permanently failed, skipped — with the full context in hand.

/ Signature

```
do_action( 'fswa_delivery_event', $event, $ctx )
```

/ When it fires

Fired by the dispatcher itself — synchronous and queued deliveries alike — at the moment it writes each state to the delivery log, so the context is complete and nothing needs to be queried back. The built-in Notifications feature is a listener on this hook at priority 10 and does no I/O of its own beyond queuing a row; keep your own listener cheap too, since it runs inside the dispatch (on a cron tick for queued webhooks, inside the request for synchronous ones). Do not dispatch another webhook from here without guarding against loops.

/ Parameters

| Name | In | Type | Req | Description |
| --- | --- | --- | --- | --- |
| $event | param | string | yes | One of \`success\`, \`recovered\`, \`failed\_attempt\`, \`retry\_scheduled\`, \`permanently\_failed\`, \`skipped\`. \`recovered\` fires right after a \`success\` whose delivery had at least one earlier failed attempt, so a listener can subscribe to either. |
| $ctx | param | array | yes | Normalized context: \`webhook\` (id, uuid, name, endpoint\_url, is\_enabled), \`trigger\`, \`log\_id\`, \`event\_uuid\`, \`event\_timestamp\`, \`attempt\` (1-based), \`max\_attempts\`, \`next\_attempt\_at\`, \`http\_code\` (null for transport errors), \`error\_message\`, \`response\_body\` (capped at 4 KB), \`duration\_ms\`, \`request\_url\`, \`is\_test\`, \`payload\` (what was sent), \`original\_payload\` (pre-mapping, or null), \`reason\` (\`exhausted\` | \`non\_retryable\` on permanently\_failed), \`had\_failures\`, \`fired\_at\`. |

/ Examples

Count permanently failed deliveries per webhook in a transient

```
add_action( 'fswa_delivery_event', function ( string $event, array $ctx ) {
    if ( $event !== 'permanently_failed' ) return;
    $key   = 'my_failed_' . (int) $ctx['webhook']['id'];
    $count = (int) get_transient( $key ) + 1;
    set_transient( $key, $count, DAY_IN_SECONDS );
    if ( $count === 3 ) {
        error_log( sprintf( '%s gave up 3 times today — last error: %s', $ctx['webhook']['name'], $ctx['error_message'] ) );
    }
}, 10, 2 );
```

/ Related

[Notifications](https://wpwebhooks.org/docs/notifications/)[fswa\_notification\_message](https://wpwebhooks.org/docs/fswa-notification-message/)[fswa\_success](https://wpwebhooks.org/docs/fswa-success/)[fswa\_error](https://wpwebhooks.org/docs/fswa-error/)[fswa\_skipped](https://wpwebhooks.org/docs/fswa-skipped/)[Delivery Logs](https://wpwebhooks.org/docs/delivery-logs/)

/Ready

## Your next automation is  
one sentence away.

[Install Plugin→](https://downloads.wordpress.org/plugin/flowsystems-webhook-actions.zip) [Try the Live Preview](https://playground.wordpress.net/?blueprint-url=https://wpwebhooks.org/blueprint.json) [See the Plugin →](https://wpwebhooks.org/wordpress-webhook-plugin/)

$ wp plugin install flowsystems-webhook-actions --activate

## Structured data

```json
{"@context":"https://schema.org","@type":"TechArticle","name":"fswa_delivery_event — Action Hook — Webhook Actions Docs","description":"Fires at every delivery state change — success, recovered, failed attempt, retry scheduled, permanently failed, skipped — with the full context in hand.","url":"https://wpwebhooks.org/docs/fswa-delivery-event/","isPartOf":{"@type":"WebSite","name":"WP Webhooks","url":"https://wpwebhooks.org"},"about":{"@type":"SoftwareApplication","name":"Webhook Actions","applicationCategory":"WordPress Plugin"}}
```
