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:
system_chatflow — chat manager fires this directly to bypass the type registry for instant low-latency push- 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_messageso the dispatcher writes the history row. - No history row. Direct
mam_notification_send_pnfires don’t producewp_mam_notification_historyentries (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 = truesends a content-available push — no UI alert, the app wakes in the background.immediate = truebypasses the cron threshold.- Failures land in
MAM_PushResult->errors, not in any return value. Direct callers should listen tomam_update_pn_status(vestigial — no current fire sites; tracked).
Related articles
- 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 |
