Hook: mam_notification_send_pn

Purpose

Push-channel hook. Fired by MAM_Pn_Sender after the dispatcher decides a message should reach push. The legacy handler mam_push_notification_manager::send_push_notification() listens here and bridges to MAM_PushDispatcher (modern OO hierarchy).

Sibling plugins normally don’t fire this directly — use mam_notification_send_message and let the dispatcher route. The exceptions are documented below.


Signature

do_action( 'mam_notification_send_pn', array $messages );
Parameter Type Description
$messages array Array of push-message arrays — each carries title, message, user_id, data, is_silent, immediate

Per-message shape:

array(
    'title'     => 'New chat message',
    'message'   => 'Hi! Are you there?',
    'target'    => 'https://...',     // permalink for deeplink
    'user_id'   => 123,
    'immediate' => true,
    'is_silent' => false,
    'data'      => array( ... ),       // arbitrary payload
)

When to fire directly

Almost never. Two legitimate cases:

  1. system_chat flow — chat manager fires this directly to bypass the type registry for instant low-latency push
  2. Custom mass-push utilities — admin-side broadcast tooling that’s already done its own opt-in resolution

In every other case, fire mam_notification_send_message instead — that goes through the dispatcher (which writes the history row, applies opt-out, and uses the type’s templates) and ends up firing this hook on your behalf.


Example: chat manager direct fire

do_action( 'mam_notification_send_pn', array( array(
    'title'     => 'New chat message',
    'message'   => $stripped_body,
    'target'    => get_permalink( $thread_post_id ),
    'user_id'   => $recipient_user_id,
    'immediate' => true,
    'is_silent' => false,
    'data'      => array( 'thread_id' => $thread_post_id ),
) ) );

This is what the dispatcher does internally for message_type = system_chat.


Send pipeline

do_action('mam_notification_send_pn', $messages)
        │
        ▼
mam_push_notification_manager::send_push_notification()  ← legacy bridge
        │ wraps each message in MAM_PushNotification
        ▼
MAM_PushDispatcher::dispatch( $notification )
        │
   ┌────┴────────────────┬─────────────────┐
   ▼                     ▼                 ▼
$notification->is_voip()? else            FCM token?
   YES                   ▼                 │
   ▼                MAM_IosPushSender    MAM_AndroidPushSender
MAM_IosVoipPushSender    APNs HTTP/2     FCM v1

See Integration: APNs and Integration: FCM.


Gotchas

  • Don’t use this for normal sibling-plugin pushes. Use mam_notification_send_message so the dispatcher writes the history row.
  • No history row. Direct mam_notification_send_pn fires don’t produce wp_mam_notification_history entries (the dispatcher writes those, and you’ve bypassed it). The in-app notification center won’t show your push.
  • No opt-out check. Direct fires bypass the per-channel opt-in check.
  • is_silent = true sends a content-available push — no UI alert, the app wakes in the background.
  • immediate = true bypasses the cron threshold.
  • Failures land in MAM_PushResult->errors, not in any return value. Direct callers should listen to mam_update_pn_status (vestigial — no current fire sites; tracked).

  • Notifications overview
  • Notification channels: email, SMS, push
  • Integration: Apple Push Notification service (APNs)
  • Integration: Firebase Cloud Messaging (FCM)
  • Hook: mam_notification_send_message
  • Frozen public contracts reference

Metadata

Field Value
Article type Hook Reference
Plugin slug mam-main
Applies to plugin version 2.1.11+
Hook type action
Audience PHP developer
Frozen contract yes
Last verified 2026-05-02
Contents

    Need Support?

    Can’t find the answer you’re looking for? Don’t worry we’re here to help!