The widget is configured by the script tag and by a global window.__m24 object that your page defines. It does not expose methods or emit events: there is no JavaScript call to open, close or reset the chat. Appearance, the welcome message and the visitor details form are set in the channel’s settings in Message24, not from your page.
Reference
Section titled “Reference”| Setting | Type | Read when |
|---|---|---|
data-channel-key (script attribute) |
string | Once, when the script runs. Required. |
window.__m24.visitor |
{ name?: string, phone?: string } |
When the widget loads and when it creates the visitor’s session. |
window.__m24.pageData |
JSON object | Every time the visitor sends a message. |
window.__m24.renderers.product |
(product) => HTMLElement |
Every time a product card renders. |
window.__m24.renderers.productList |
(products) => HTMLElement |
Every time a message with product cards renders. |
Always extend the object rather than replacing it, so separate scripts do not overwrite each other:
window.__m24 = window.__m24 || {};Visitor identity
Section titled “Visitor identity”If your site knows who the visitor is, pass their name and phone so the conversation in the inbox is labelled with them instead of “Visitor”:
<script> window.__m24 = window.__m24 || {}; window.__m24.visitor = { name: "Alice", phone: "+964 750 000 0000" };</script><script src="https://message24.net/widget.js" data-channel-key="YOUR_CHANNEL_KEY" async></script>How it is used:
- Set it before the widget script loads. The widget reads it once while starting up.
- It is captured when the visitor first opens the chat in that browser, which is when the session is created. The name and phone are applied to the contact when the first message is sent. Changing
window.__m24.visitorafterwards does not update an existing conversation. - If the channel has a visitor details form, the form is still shown, pre-filled with these values, and the visitor confirms or edits them.
- Limits:
nameup to 120 characters,phoneup to 40. The phone number is normalized by Message24; one that cannot be read as a phone number makes session creation fail with400.
Without visitor and without a visitor details form, the conversation appears as “Visitor”.
Page context
Section titled “Page context”Tell the AI agent and the team what the visitor is looking at. Set window.__m24.pageData to any JSON object:
<script> window.__m24 = window.__m24 || {}; window.__m24.pageData = { pageType: "product", productId: "sku_123", name: "Blue Sneakers", category: "Shoes", price: 79.99, currency: "USD", inStock: true };</script>With each message, the widget sends a page context made of:
| Field | Value |
|---|---|
url |
location.pathname at send time. Maximum 2,048 characters. |
title |
document.title at send time. Maximum 300 characters. |
data |
window.__m24.pageData at send time, if set. Must be valid JSON, 8 KB at most. |
The page context is stored on the message and as the conversation’s current page.
The AI agent gets a one-line summary of the current page built from these keys in data, plus url:
| Summary item | Keys read from data, first one present wins |
|---|---|
| Page | name, productName, title, product_title, then the page title |
| Price | price or amount, with currency or currencyCode |
| Stock | inStock or available (boolean) |
| Category | category |
| Type | pageType or type |
Other keys, such as productId above, are stored with the message but are not part of that summary.
In a single-page app, update pageData as the route changes. See Single-page apps and route changes.
Custom product cards
Section titled “Custom product cards”When the AI agent sends products, the widget shows product cards: an image, the name, price, a stock badge, and a View link when the business has product links set up. You can render your own cards instead, for example to add an “Add to cart” button wired to your store.
Register a renderer on window.__m24.renderers. It receives a product object and must return an HTMLElement:
<script> window.__m24 = window.__m24 || {}; window.__m24.renderers = { // Replace individual product cards product: function (product) { // product = { name, price, currency, imageUrl, url, inStock, externalId } var card = document.createElement("div"); card.style.cssText = "border:1px solid var(--m24-border);border-radius:12px;overflow:hidden;background:var(--m24-surface);";
if (product.imageUrl) { var img = document.createElement("img"); img.src = product.imageUrl; img.alt = product.name; img.style.cssText = "width:100%;height:160px;object-fit:cover;display:block;"; card.appendChild(img); }
var info = document.createElement("div"); info.style.padding = "12px";
var name = document.createElement("div"); name.textContent = product.name; name.style.cssText = "font-weight:600;font-size:14px;color:var(--m24-text);"; info.appendChild(name);
var row = document.createElement("div"); row.style.cssText = "display:flex;align-items:center;justify-content:space-between;margin-top:8px;";
var price = document.createElement("div"); price.textContent = new Intl.NumberFormat(undefined, { style: "currency", currency: product.currency || "USD", minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(product.price); price.style.cssText = "font-weight:700;font-size:15px;color:var(--m24-text);"; row.appendChild(price);
var btn = document.createElement("button"); btn.textContent = "Add to Cart"; btn.style.cssText = "border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600;cursor:pointer;background:var(--m24-accent);color:#fff;"; btn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); // Wire to your cart API here btn.textContent = "Added!"; btn.disabled = true; }); row.appendChild(btn);
info.appendChild(row); card.appendChild(info); return card; },
// Or replace the entire product list container (optional) // productList: function (products) { // var grid = document.createElement("div"); // grid.style.cssText = "display:grid;grid-template-columns:1fr 1fr;gap:8px;"; // products.forEach(function (p) { ... }); // return grid; // } };</script>renderers.product replaces each card inside the widget’s own layout; a message with more than one product is shown as a horizontal carousel. renderers.productList receives all of a message’s products as an array and replaces the whole group, so you control the layout. If both are set, productList wins.
Product fields
Section titled “Product fields”| Field | Type | Description |
|---|---|---|
name |
string | Product name. |
price |
number | Selling price. For a product with variants, the lowest variant price. |
currency |
string | The business’s currency code, for example "IQD" or "USD". |
imageUrl |
string, optional | First product image. A signed, time-limited URL: display it, don’t store it. |
url |
string, optional | Link to the product on the business’s website. Present only when the business has set up product links. |
inStock |
boolean | Stock availability. |
externalId |
string | The product’s ID in the business’s own catalog, useful for calling your cart API. |
CSS variables
Section titled “CSS variables”Your elements render inside the widget’s Shadow DOM, so your site’s stylesheets do not apply to them. Use inline styles, and these variables to match the widget:
var(--m24-accent) /* Brand colour, from the channel's Primary Color */var(--m24-text) /* Main text: #1a1a1a */var(--m24-text-muted) /* Muted text: #8b8b8b */var(--m24-surface) /* Background: #ffffff */var(--m24-surface-alt) /* Alt background: #f4f5f7 */var(--m24-border) /* Border: #e8e8e8 */Errors
Section titled “Errors”If a renderer throws, the widget logs [m24] Custom product renderer error to the console. If it returns something that is not an HTMLElement, it logs [m24] Custom product renderer must return an HTMLElement. In both cases the card area stays empty; the widget does not fall back to its default card, so test your renderer against real products.
- Call
e.stopPropagation()in button handlers inside a card so the click does not reach anything underneath. - Define
window.__m24.renderersbefore the widget script loads, so the first cards already use it. - To render with React or Vue, create a container element, mount your component into it with
createRootorcreateApp, and return the container. - Try your renderer on the test page before deploying.
Links in messages
Section titled “Links in messages”Replies can contain Markdown links ([text](url)) and bare http or https URLs, which the widget turns into links and link cards. A link to the same host as the page opens in the same tab; any other link opens in a new tab. javascript:, data:, vbscript: and file: links are shown as plain text.
