This reference is for developers extending Order Messenger for WooCommerce: PHP services, hooks, templates, styling, JavaScript, the REST API and shortcodes. For setup and day-to-day use see the user documentation.
Services
↑ Back to topThe plugin lives in the U2Code\OrderMessenger namespace. Services are registered in a container and reached through typed getters:
use U2Code\OrderMessenger\Core\ServiceContainer;
$container = ServiceContainer::getInstance();
$container->getMessageService(); // write messages
$container->getMessageRepository(); // read and query messages
$container->getAttachments(); // uploads and the download endpoint
$container->getNotifications(); // email notifications
$container->getScheduler(); // Action Scheduler jobs
$container->getPermissions(); // who may do what
$container->getSettings(); // the WooCommerce settings tab
Messages are rows of the {prefix}wc_order_messages table. Dates are stored in UTC.
| Column | Meaning |
|---|---|
| id | Message id. |
| order_id | The order the conversation belongs to. |
| user_id | The order’s customer id, 0 for guests. |
| sender_id | The user who wrote the message, 0 for system entries. |
| attachment_id | Media library id of the attachment, or NULL. |
| message | Plain text. |
| type | 0 team, 1 customer, 2 order note, 3 status change, 4 scheduled (waiting), 5 scheduled (sent). |
| date_sent, date_read | UTC timestamps; date_read is NULL while unread. |
| is_notified | 1 once an email went out or none was needed. |
| data | JSON: scheduled_date, notify, from and to for status changes, note_id, purchase_product_id. |
Messages from code
↑ Back to topuse U2Code\OrderMessenger\Core\ServiceContainer;
use U2Code\OrderMessenger\Entity\MessageType;
use U2Code\OrderMessenger\Services\MessageException;
$container = ServiceContainer::getInstance();
$order = wc_get_order( 123 );
try {
// From the store to the customer. Sender 0 shows the store signature.
$container->getMessageService()->sendAdminMessage( $order, get_current_user_id(), 'Your parcel ships tomorrow.', array(
'notify' => true, // email the customer
'attachment_id' => 0, // a media library id
'schedule_at' => new DateTimeImmutable( '+2 days 10:00', wp_timezone() ), // omit to send now
) );
// From the customer. Permissions are checked: the customer, or a guest with the order key.
$container->getMessageService()->sendCustomerMessage( $order, $order->get_customer_id(), 'Thanks!' );
// A system entry, for example from a shipping plugin.
$container->getMessageService()->createSystemMessage( $order, MessageType::service(), 'Shipped with DHL, tracking 123456.', array( 'tracking' => '123456' ) );
} catch ( MessageException $e ) {
// $e->getCode() is the HTTP status the REST API would answer with: 400, 403 or 429.
error_log( $e->getMessage() );
}
// Reading.
$repository = $container->getMessageRepository();
$messages = $repository->getForOrder( $order->get_id(), array( 'context' => 'admin', 'limit' => 50 ) );
$unread = $repository->countUnreadForOrder( $order->get_id(), 'admin' ); // waiting for the team
$repository->markReadForOrder( $order->get_id(), 'admin' );
foreach ( $messages as $message ) {
$message->getMessage(); // text
$message->getMessageType()->isCustomer(); // who wrote it
$message->getDateSent(); // DateTimeImmutable in the site timezone
$message->getAttachment(); // MessageAttachment or null
}
The context is admin for the team’s view and customer for the customer’s. It decides which message types are included and whose unread state is meant.
Hooks
↑ Back to topAll hook names start with order_messenger/. The most used ones are listed here, grouped by purpose. The complete set is in the plugin source: search it for apply_filters and do_action.
Actions
↑ Back to top| Action | Arguments | When |
|---|---|---|
| messages/message_created | Message $message | A message was stored, any type. |
| messages/message_updated | Message $message | A message changed: read state, publish. |
| messages/before_delete, messages/message_deleted | Message $message | Before and after a message is deleted. |
| messages/changed | ?Message $message, int $orderId | Anything changed in an order’s conversation; the unread counters listen here. |
| messages/scheduled_message_published | Message $published, Message $scheduled | A scheduled message went out. |
| notifications/sent | int $orderId, Message[] $messages, string $side, bool $sent | A notification email was attempted; $side is admin or customer. |
| attachments/uploaded | int $attachmentId, WC_Order $order, array $result | A customer upload was stored. |
| admin/product_page/messenger_tab_begin, messenger_tab_end | int $productId | Add fields to the product’s Order Messenger tab. |
| rest/register_routes | string $namespace | Register your own routes next to the plugin’s. |
| upgraded | string $from, string $to | After a data migration. |
Permissions
↑ Back to top| Filter | Arguments | Purpose |
|---|---|---|
| permissions/capability | string $capability | The capability that marks team members, manage_order_messenger by default. |
| permissions/can_manage | bool $allowed, int $userId | Whether a user is part of the team. |
| permissions/userCanViewMessenger | bool $allowed, WC_Order $order, int $userId | Whether a customer may open a conversation. |
| permissions/userCanSendMessage | bool $allowed, int $userId, WC_Order $order, string $text | Whether a customer may write. |
| permissions/userCanSendAdminMessage | bool $allowed, int $userId, WC_Order $order | Whether a team member may write to this order. |
| permissions/userCanDeleteMessage, userCanUnreadMessage | bool $allowed, Message $message | Message actions. |
| settings/allowed_roles_to_manage_admin_messenger/default | string[] $roles | Default team roles. |
Messages and content
↑ Back to top| Filter | Arguments | Purpose |
|---|---|---|
| messages/before_saving_customer_message | Message $message, WC_Order $order, ?array $file | Change a customer message before it is stored. |
| messages/before_saving_admin_message | Message $message, $request, array $customData, WC_Order $order, int $attachmentId | Change a team message before it is stored. |
| messages/before_saving_system_message | Message $message, WC_Order $order | Change an automatic entry. |
| messages/before_publishing_scheduled_message | Message $published, Message $scheduled | Change a scheduled message as it goes out. |
| message/sender_name | string $name, Message $message | The name shown for a message. |
| message/formatted_text | string $html, ?string $text | The rendered text: links, line breaks. |
| message/max_length | int $length | Characters allowed per message, on top of the setting. |
| system_messages/post_order_note | bool $post, $note, WC_Order $order | Skip specific order notes. |
| config/rate_limit | array $limit | Customer messages per window: limit and window in seconds. |
| config/message_types_customer_should_be_notified, config/message_types_admin_should_be_notified | int[] $types | Which message types trigger emails for each side. |
| notification/email_delay | int $minutes | The digest delay. |
Settings, templates and storefront
↑ Back to top| Filter | Arguments | Purpose |
|---|---|---|
| settings/sections | SectionAbstract[] $sections | Add a settings section. |
| settings/settings | array $fields, SectionAbstract $section | Change the fields of a section. |
| settings/available_themes | array $palettes | Add a color palette: name to custom properties. |
| settings/available_file_types | array $types | Add an attachment type: key to title, mime and ext. |
| config/get_{key} | mixed $value, array $args | Every setting passes through a filter named after it, for example config/get_polling_interval. |
| template/location | string $file, string $template | Where a template is loaded from. |
| message/message_template_page | string $path, string $place, Message $message | The template used for a message. |
| frontend/conversation_config | array $config, WC_Order $order | The data attributes the storefront script reads. |
| frontend/show_on_order_received | bool $show, WC_Order $order | Show the conversation on the order confirmation page. |
| frontend/theme_integration_css | string $css | Extra CSS printed with the storefront stylesheet. |
| order/customer_name, order/summary | string or array, WC_Order $order | How an order is described in the admin app and the emails. |
| attachments/sideload | bool $sideload, array $file, ?WC_Order $order | Accept files that are not browser uploads: imports, tests. |
| rest/features | array $features, WC_Order $order, bool $isManager | What the composer offers for an order and viewer. |
| rest/message | array $data, Message $message, string $context | The JSON shape of a message. |
| admin/app_config | array $config | The configuration passed to the admin app. |
Examples
↑ Back to top// Post a message when an order is completed.
add_action( 'woocommerce_order_status_completed', function ( $order_id ) {
$order = wc_get_order( $order_id );
\U2Code\OrderMessenger\Core\ServiceContainer::getInstance()->getMessageService()
->sendAdminMessage( $order, 0, 'Your order is complete. Thank you for shopping with us!', array( 'notify' => true ) );
} );
// Show a role name instead of the team member's name.
add_filter( 'order_messenger/message/sender_name', function ( $name, $message ) {
return $message->getMessageType()->isFromStore() ? 'Support team' : $name;
}, 10, 2 );
// Allow SVG attachments.
add_filter( 'order_messenger/settings/available_file_types', function ( $types ) {
$types['svg'] = array( 'title' => 'SVG', 'mime' => 'image/svg+xml', 'ext' => 'svg' );
return $types;
} );
// Keep the conversation off the order confirmation page.
add_filter( 'order_messenger/frontend/show_on_order_received', '__return_false' );
// Let customers write only while the order is not completed.
add_filter( 'order_messenger/permissions/userCanSendMessage', function ( $allowed, $user_id, $order ) {
return $allowed && ! $order->has_status( 'completed' );
}, 10, 3 );
Templates
↑ Back to topStorefront templates live in the plugin’s views/frontend folder. Copy them into your theme under order-messenger-for-woocommerce/, keeping the path below frontend/:
your-theme/order-messenger-for-woocommerce/my-account/messenger.php
your-theme/order-messenger-for-woocommerce/my-account/dialogs.php
your-theme/order-messenger-for-woocommerce/my-account/no-access.php
your-theme/order-messenger-for-woocommerce/my-account/no-conversations.php
your-theme/order-messenger-for-woocommerce/message/message-attachment.php
your-theme/order-messenger-for-woocommerce/message/message-types/admin-message.php
your-theme/order-messenger-for-woocommerce/message/message-types/customer-message.php
your-theme/order-messenger-for-woocommerce/message/message-types/service-message.php
your-theme/order-messenger-for-woocommerce/message/message-types/order-status-message.php
your-theme/order-messenger-for-woocommerce/message/message-types/scheduled-message.php
The conversation template receives $order, $orderId, $orderKey, $messages (oldest first), $totalMessages, $hasMore, $canSend, $filesEnabled, $config (the data attributes the script reads) and $presenter. Message templates receive $message, $order and $orderKey. Keep the data-om-* attributes and the om-* classes if the script should keep working: it sends messages, polls for new ones and opens the lightbox through them.
The notification emails are regular WooCommerce templates. Copy them from the plugin’s views/emails folder to your-theme/woocommerce/emails/: admin-messenger-notification.php, customer-messenger-notification.php, partials/messages.php and the plain/ variants.
The lookup can be redirected with the order_messenger/template/location filter, which receives the resolved file and the template name.
Styling
↑ Back to topThe customer-side conversation is theme-neutral on purpose: it inherits the page font, derives its lines and fills from the current text color and uses the theme’s own inputs and buttons. The named palettes set custom properties on .om-messenger and .om-conversations; set them yourself for a custom look:
.om-messenger {
--om-primary-color: #2b6cb0; /* store bubbles */
--om-primary-text-color: #ffffff;
--om-secondary-color: #f1f5f9; /* customer bubbles */
--om-secondary-text-color: #0f172a;
--om-radius: 12px;
}
Useful classes: .om-messenger (the whole widget), .om-message with the modifiers –customer, –admin, –system and –own, .om-message__bubble, .om-message__meta, .om-attachment, .om-messenger__composer, .om-badge and .om-nav-badge (unread counts) and .om-conversations (the My Account list). Theme-specific rules can be added with the order_messenger/frontend/theme_integration_css filter, which is how the plugin gives Storefront a chat icon in the account menu.
JavaScript
↑ Back to topThe storefront script has no dependencies. Each element with the data-om-messenger attribute gets a Conversation instance, available as element.omConversation, with poll(), loadOlder() and send(). window.omFrontendConfig carries the polling interval and the unread count for the account menu badge. The polling interval is the Live updates setting; 0 switches polling off.
The admin app is a React application built on the WordPress components package. Its configuration is window.omMessengerConfig, filterable through order_messenger/admin/app_config, and the composer’s options for a given order come from the rest/features filter.
REST API
↑ Back to topNamespace order-messenger/v1. Requests from the site itself authenticate with the WordPress cookie and an X-WP-Nonce header (the wp_rest nonce); external clients use application passwords. Customers reach only their own orders; guests pass the order key as key. Team routes require the manage_order_messenger capability.
| Route | Method | Parameters |
|---|---|---|
| /orders/{id} | GET | key for guests. Returns the order summary, unread count and the features the composer may offer. |
| /orders/{id}/messages | GET | before_id, after_id, per_page (up to 200), render = json or html. Newest page first; after_id is what live updates poll with. |
| /orders/{id}/messages | POST | message, file (multipart, customers), attachment_id (team), notify, schedule_at (Y-m-d\TH:i:s in site time), custom_data, render. Answers 201 with the message. |
| /orders/{id}/read | POST | Mark the viewer’s side of the conversation as read. |
| /messages/{id} | DELETE | Team only. |
| /messages/{id}/unread | POST | Team only. |
| /conversations | GET | Team only. search, unread_only, page, per_page (up to 100). |
| /unread-count | GET | Team only. Unread customer messages across all orders. |
// From a page on the same site.
const response = await fetch( wpApiSettings.root + 'order-messenger/v1/orders/123/messages', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings.nonce },
body: JSON.stringify( { message: 'Hello from the storefront' } ),
} );
const { message } = await response.json();
Errors use the usual WordPress REST shape with the HTTP status: 400 for an empty or invalid message, 403 for a viewer outside the conversation, 404 for an unknown order, 429 when a customer exceeds the rate limit. The routes of version 3 (/get/{limit}/{offset}, /send, /sendAdmin, /delete/{id}, /unread/{id}) still answer with their old shapes.
Shortcodes and endpoints
↑ Back to top| Item | Details |
|---|---|
| [order_chatroom order_id=””] | One conversation; falls back to the view-order or messenger query var. |
| [order_chatrooms limit=”10″ current_page=”1″] | The current customer’s conversations. |
| [order_chatroom_unread_messages_count order_id=””] | Unread count for an order, or for the customer when empty. |
| My Account endpoints | messages (the list) and messenger/{order-id} (one conversation), registered through woocommerce_get_query_vars. |
| Admin page | admin.php?page=om-messenger&order={id} opens a conversation on the Messenger page. |
Extending the admin
↑ Back to top- Product tab: print extra fields between the admin/product_page/messenger_tab_begin and messenger_tab_end actions and save them on woocommerce_process_product_meta.
- Settings: add a section with settings/sections (a class extending Settings\Sections\SectionAbstract) or add fields to an existing one with settings/settings. Every value is then available through Config and the config/get_{key} filter.
- Composer features: rest/features decides, per order and viewer, whether attachments, scheduling and the email toggle are offered.
- Order summary: order/summary and order/customer_name change how an order is shown in the conversation header and the emails.
- Messages: before_saving_admin_message receives the REST request and the custom_data array, so a custom composer field can be stored in the message’s data and rendered with message/message_template_page.
- Order actions: the Send purchasing message order action runs the purchase messages addon; call createSystemMessage() from your own actions for other automation.
Debugging
↑ Back to top- Background jobs: WooCommerce → Status → Scheduled Actions, group order-messenger. order_messenger/publish_scheduled_message runs once per scheduled message, order_messenger/publish_due_scheduled_messages every 10 minutes as a catch-up (scheduler/catch_up_interval), order_messenger/send_notification_digests while digests are enabled (notifications/digest_interval).
- Notifications: the notifications/sent action reports every attempt with the side and whether wp_mail() accepted it. Digests skip messages that were read in the meantime.
- Uploads: customer files live under uploads/order-messenger/ behind a deny rule and are served through admin-post.php?action=om_attachment with a nonce tied to the attachment. Use AttachmentService::getDownloadUrl( $attachment, $size, $orderKey ) to build links; a 403 from that endpoint means the viewer is not part of the conversation.
- Rate limit: customers may send 30 messages per 10 minutes by default; the REST API answers 429. Change it with config/rate_limit.
- Settings values: every setting can be overridden for a request with its config/get_{key} filter, which is the quickest way to test a configuration without saving it.
- Templates: wrap your override in the template/before_render and template/after_render actions to confirm it is the one being loaded.
Support
↑ Back to topFor questions about the plugin, contact support with your WooCommerce.com account.