nimbly_mail_mail_sent — Action After Successful Send

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) and nimbly_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

id is 0 when email logging is disabled in NimblyMail settings, or when the log row insertion failed. Consumers must not assume id > 0. If you need to correlate with a specific log row, check $log_data['id'] > 0 before 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: 0 edge case — guard against this if you intend to update the log row from your handler.
  • size_bytes is 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_data to untrusted output without sanitisation.
  • Handlers registered here should be idempotent where possible — NimblyMail does not de-duplicate fired actions.

See Also

Was this article helpful?