Hook: mam_chat_add_to_chat_thread

Signature

do_action( 'mam_chat_add_to_chat_thread', array $args );
Parameter Type Description
$args array Message-and-thread bundle. See the table below.

$args keys:

Key Type Required Description
message string yes The message body. Trimmed; empty messages are silently dropped.
conversation_id string one of conversation_id / participant set The opaque mam: token for the thread. Preferred when known.
post_id int with participant set Context post ID. Used to build the token if conversation_id is absent.
user_ids int[] with participant set WP user IDs of explicit individual participants.
group_ids int[] with participant set mam-chat-group post IDs.
sender_id int optional WP user ID of the message sender. Defaults to mam_user_id() (the current MAM session).

Returns: nothing — this is an action, not a filter. The handler returns an internal status array, but action subscribers cannot read the return value.


Purpose

mam_chat_add_to_chat_thread lets server-side code post a chat message on a user’s behalf. Common use cases:

  • Posting an automated welcome message into a thread the user just opened
  • Forwarding an external event (a quote acceptance, a status change, an order update) into a thread as a system message attributed to a real user
  • Bulk-seeding messages from a migration script

The action does the same work as the user-facing mam_chat_send_message API endpoint: it resolves the thread (creating it if needed), inserts a mam-chat-message CPT, marks the message as already read for the sender, and dispatches push notifications to every other participant via mam_notification_send_message.


When it runs

Whenever any code calls do_action( 'mam_chat_add_to_chat_thread', $args ). The plugin registers mam_chat_manager_endpoints::add_to_chat_thread() at default priority (10).

Inside the handler:

  1. The message is trimmed; empty messages return false immediately and no message is posted.
  2. If conversation_id is provided, it is used directly. Otherwise the handler builds a token from post_id + user_ids + group_ids (after converting WP user IDs to app_user IDs).
  3. The thread is resolved with create=true — if no thread matches the token, a new mam-chat-thread CPT is inserted, its mam_thread_key is populated, and the participant index is built.
  4. The sender_id is resolved (falling back to mam_user_id()), converted to its app_user ID. If no sender can be resolved, the call returns false.
  5. A mam-chat-message CPT is inserted as a child of the thread, with post_author = sender app_user ID and post_content sanitized via sanitize_text_field().
  6. The thread’s mam_thread_last_read_{sender} pointer is advanced to the new message ID — so the sender does not see the message they just posted as unread.
  7. A canonical conversation_id is rebuilt from the persisted thread metadata, and a push payload is assembled for every participant except the sender. If mam_notification_send_message has at least one subscriber, the push is dispatched.

Example: post a welcome message into a fresh thread

do_action(
    'mam_chat_add_to_chat_thread',
    [
        'message'   => 'Welcome to Acme Marketplace! Tap a listing to start a conversation with the seller.',
        'post_id'   => 0,
        'user_ids'  => [ $welcome_bot_wp_user_id, $new_user_wp_user_id ],
        'group_ids' => [],
        'sender_id' => $welcome_bot_wp_user_id,
    ]
);

If a thread between the welcome-bot user and the new user does not yet exist, this call creates one, posts the message, and pushes a notification to the new user.


Example: post a system update into an existing listing thread

$conversation_id = apply_filters(
    'mam_get_thread_id_for_conversation',
    0,
    [ $buyer_app_user_id ],
    [ $listing_staff_group_id ],
    $listing_post_id,
    false
);

do_action(
    'mam_chat_add_to_chat_thread',
    [
        'conversation_id' => $conversation_id,
        'message'         => 'Your inquiry has been routed to our weekend team.',
        'sender_id'       => $listing_owner_wp_user_id,
    ]
);

When the conversation_id is known up-front, pass it directly — the handler skips the participant-set resolution path entirely.


Example: forward an order-status webhook

add_action( 'my_app_order_status_changed', function ( $order_id, $new_status ) {
    $order        = wc_get_order( $order_id );
    $vendor_id    = (int) get_post_field( 'post_author', $order->get_items()[0]->get_product_id() );
    $customer_id  = $order->get_customer_id();

    do_action(
        'mam_chat_add_to_chat_thread',
        [
            'message'   => sprintf( 'Order #%d is now %s.', $order_id, $new_status ),
            'post_id'   => $order_id,
            'user_ids'  => [ $vendor_id, $customer_id ],
            'sender_id' => $vendor_id,
        ]
    );
}, 10, 2 );

Gotchas

  • Empty messages are silently dropped. An '' or whitespace-only message logs nothing; the action returns without error. If you’re debugging a missing system message, check that the message body actually has content after trim().
  • The sender must be a real MAM user. sender_id is converted to an app_user post ID. If the WP user has no corresponding app_user CPT, the call drops without sending. The simplest workaround is to seed a dedicated “system” WP user and ensure it has a matching app_user record.
  • Push notifications go through mam_notification_send_message. If no subscriber is attached to that action (e.g. on a development site without push wired up), no push is dispatched but the message is still inserted. The mobile client will see the new message on the next refresh; the user just won’t get a banner.
  • The sender’s read pointer advances automatically. The plugin marks the new message as read for the sender so the sender does not see their own outbound message as unread. Recipients’ unread counts update on their next phone-data request.
  • conversation_id is rebuilt before push dispatch. The token sent in the push payload is recomputed from the persisted mam_thread_users and mam_thread_groups of the thread, not from whatever you passed in $args. This is intentional — it keeps the mobile client’s cached conversation_id in sync if the thread participants have drifted.
  • Always pass at least one of conversation_id or a participant set. Passing only a message produces a thread keyed mam:eyJ2IjoyLCJwb3N0X2lkIjowLCJ1c2VycyI6W10sImdyb3VwcyI6W119 — the empty-payload bucket. Multiple unrelated callers will all post into the same orphan thread.

Verification

This article was last verified against:

  • Plugin: mam-chat-manager v2.0.0
  • Source: includes/mam_chat_manager_end_points.php (add_to_chat_thread())

Re-verify whenever the $args key set changes, the participant-set token build path is moved, the push-payload shape changes, or the sanitization on post_content (sanitize_text_field()) is replaced.


  • Plugin: mam-chat-manager
  • Hook: mam_add_chat_to_detail — resolve the conversation_id you’ll pass back into this action
  • Hook: mam_chat_set_chat_user_name — control the name shown on the new message
  • Hook: mam_chat_manager_get_user_avatar — control the avatar shown on the new message

Metadata

Field Value
Article type Hook Reference
Plugin slug mam-chat-manager
Applies to plugin version 2.0.0+
Category Extending MAM Suite
Hook type action
Audience PHP developer
Last verified 2026-05-01
Contents

    Need Support?

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