Type: Action
Arguments: 2
Fired in: Processor::handle_success() (via wp_mail_succeeded)
Since: 1.0.0
Overview
Fires after every successful wp_mail() delivery. Provides a normalised payload matching the shape Logger writes to the database, so analytics handlers and rollups do not need a second query to the nimbly_mail_logs table.
Important
This action fires only on success. Delivery failures continue to trigger
wp_mail_failed(WordPress core) andnimbly_mail_mail_failed(NimblyMail).
Signature
/**
* @param array $log_data Normalised send payload. Shape:
* - id int Log row id (0 if logging disabled or insert failed).
* - to string Recipients, comma-separated.
* - subject string Subject line (≤255 chars).
* - mailer string The mailer slug that handled the send.
* - sent_at int Unix timestamp.
* - size_bytes int Approximate message size in bytes.
* - source string Calling context identifier (plugin slug, theme, etc.).
* @param AbstractMailer $mailer The mailer that handled the send.
*/
do_action( 'nimbly_mail_mail_sent', $log_data, $mailer );
Payload Shape (NimblyMailLogPayload)
| Key | Type | Description |
|---|---|---|
id |
int |
DB log row ID. May be 0 — see note below. |
to |
string |
All recipients, comma-separated. |
subject |
string |
Subject line, truncated at 255 characters. |
mailer |
string |
Mailer slug (php, smtp, gmail, ses, resend, sendlayer, or a custom slug). |
sent_at |
int |
Unix timestamp at send time (via time()). |
size_bytes |
int |
strlen() of the message body. Approximate — excludes binary attachments. |
source |
string |
Calling context: plugin slug, theme:<slug>, or WordPress. |
Note
idis0when email logging is disabled in NimblyMail settings, or when the log row insertion failed. Consumers must not assumeid > 0. If you need to correlate with a specific log row, check$log_data['id'] > 0before using it.
Payload Contract
The payload shape above is stable and treated as public API. Future changes require a @since bump and a deprecation notice. Do not access array keys not listed above — new keys may be added in future versions.
Example — Real-Time Counter in a Transient
<?php
/**
* Plugin Name: My Email Reports (mu-plugin example)
*/
add_action(
'nimbly_mail_mail_sent',
function ( array $log_data, $mailer ): void {
$key = 'my_plugin_sent_count_' . gmdate( 'Y-m-d' );
$current = (int) get_transient( $key );
set_transient( $key, $current + 1, DAY_IN_SECONDS );
},
10,
2
);
Example — Per-Mailer Aggregation
<?php
add_action(
'nimbly_mail_mail_sent',
function ( array $log_data, $mailer ): void {
$key = 'my_plugin_mailer_stats';
$stats = get_transient( $key ) ?: array();
$slug = $log_data['mailer'];
$stats[ $slug ] = ( $stats[ $slug ] ?? 0 ) + 1;
set_transient( $key, $stats, HOUR_IN_SECONDS );
},
10,
2
);
Common Pitfalls
- Do not perform slow database writes in this hook — it fires in the normal request path and blocks the caller until your handler returns. Queue heavy work with
wp_schedule_single_event()or a background job. id: 0edge case — guard against this if you intend to update the log row from your handler.size_bytesis approximate — it reflects the raw message body string length before MIME encoding. Do not use it for billing-quality byte accounting.
Security Notes
- This hook fires inside an already-gated request context. No additional capability checks are required, but you should not expose
$log_datato untrusted output without sanitisation. - Handlers registered here should be idempotent where possible — NimblyMail does not de-duplicate fired actions.
See Also
nimbly_mail_active_mailer— filter which mailer handles the sendnimbly_mail_pre_send— short-circuit before deliverynimbly_mail_mail_failed— react to delivery failure
