Notification history and opt-out

Summary

Every dispatched notification is recorded in wp_mam_notification_history. The in-app notification center (Open Notifications content class) reads from it. Per-user opt-out is enforced before send.


wp_mam_notification_history

Owned by MAM_Notification_History_Repository. Renamed from wp_tsl_local_app_notifications_history in PR #31. Frozen — do not rename in place.

Per-row schema (approximate):

Column Purpose
id Primary key
user_id Recipient WP user id
message_type Slug from the type registry
subject Title line
message Body text
channel email / sms / push / in_app
status sent / failed / queued / read
created_at Insert timestamp
read_at Null until the user marks it read
data Serialized extra payload (push deeplink, action button data, etc.)

A single mam_notification_send_message fire that touches three channels produces three rows (one per channel).


In-app notification center

The Open Notifications content class reads via MAM_Notification_History_Repository::for_user($user_id, $limit) and renders rows ordered by created_at DESC. The unread_count is WHERE read_at IS NULL.

Marking-read on open issues:

UPDATE wp_mam_notification_history
   SET read_at = NOW()
 WHERE user_id = ? AND read_at IS NULL

This is a write that runs every time the user opens the notification screen — high-frequency open events have measurable DB load. Tracked.


Opt-out: email

Per-user opt-out is stored in wp_mam_email_opted_out (or per-user meta — varies by site). Before sending, MAM_Email_Sender checks:

if ( $this->is_opted_out( $user_id, $email ) ) {
    // skip; record as opted_out in history
    return;
}

Opt-outs are populated from:

  • The unsubscribe link in outbound emails
  • An admin override in Mobile App Manager → Notifications → Opt-Outs
  • A per-user toggle in the user’s profile

Opt-out: SMS

SMS opt-out is provider-managed. Twilio responds to “STOP” replies by suppressing future messages from that number. mam-main appends the STOP-to-opt-out copy automatically; subsequent attempts to send to a STOPped number return a Twilio error.

mam-main does not maintain its own SMS opt-out table — Twilio is the source of truth.


Opt-out: push

Push has no opt-out table. The user opts out by:

  • Revoking push permission in iOS / Android Settings (the OS stops delivering)
  • Uninstalling the app (token becomes invalid)
  • Explicitly disabling in-app push (sets usermeta push_enabled = 0)

Send attempts to a revoked-permission token return BadDeviceToken from APNs / Unregistered from FCM. The token repository should drop the stored token on these errors (tracked as a hardening item).


Outbox viewer

Mobile App Manager → Notifications → Outbox uses mam_notifications_outbox_viewer (a WP_List_Table subclass) to surface recent dispatches across all channels with their statuses. Useful for diagnosing delivery failures.


Gotchas

  • History rows are not deleted automatically. A high-traffic site accumulates rows indefinitely. If you implement pruning, do it via the repository — direct DELETE on the table risks corrupting indexes used by the in-app notification fetch.
  • A failed channel still produces a history row. The status column distinguishes; the in-app center should filter to status IN (sent, read) to hide failures.
  • Opt-out is checked at send time, not at queue time. A user who opts out between queue and send (cron tick later) has the opt-out honored.
  • PR #31 rename: backward-compat aliases mean direct $wpdb reads against the legacy wp_tsl_local_app_notifications_history table name no longer work. Use the repository.

  • Notifications overview
  • Notification channels: email, SMS, push
  • Notification queue and cron
  • Content class: Open Notifications
  • Frozen public contracts reference

Metadata

Field Value
Article type Plugin Overview
Plugin slug mam-main
Applies to plugin version 2.1.11+
Category Plugin Reference
Audience PHP developer
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!