Website developers · JavaScript SDK

Widget API

Connect your website forms and buttons to MatnBot chat through window.MatnBotWidget. Start conversations, share visitor context and control the floating widget.

Public reference with illustrative examples. This page does not run chat requests or expose any account's bot settings, conversations or credentials.

Quick start

  1. Choose an active, public bot. In its control panel, open the overview's Share & embed section and copy the widget code.
  2. Include the script once on your HTTPS page. Replace your-bot-slug with the slug from your own embed code.
  3. Use the SDK after ready(). It manages the visitor session internally; no server API key belongs in your page.
<script src="https://matnbot.com/widget/embed.js" data-bot="your-bot-slug"></script>

Call these commands from your own button handler:

await window.MatnBotWidget.ready();
await window.MatnBotWidget.open();
await window.MatnBotWidget.setDraft("I would like to ask about a service.");
// No message is sent until the visitor submits it.

The widget remains a floating panel. This API does not create an inline chat surface.

Connect a website form

A complete minimal form with safe retries. Adjust its fields to match the bot's required name, email and phone settings. The example disables page tracking until your consent manager enables it.

<form id="contact-form">
  <label>Email (optional)
    <input name="email" type="email" maxlength="254" autocomplete="email">
  </label>
  <label>Message
    <textarea name="message" required maxlength="10000"></textarea>
  </label>
  <button type="submit">Start chat / ابدأ المحادثة</button>
  <p id="form-status" role="status" aria-live="polite"></p>
</form>

<script src="https://matnbot.com/widget/embed.js" data-bot="your-bot-slug"></script>
<script>
  const form = document.querySelector("#contact-form");
  const status = document.querySelector("#form-status");
  const submit = form.querySelector("button");
  const message = form.elements.namedItem("message");
  const email = form.elements.namedItem("email");
  let pending;

  // Set this immediately after loading the widget, before restoration.
  window.MatnBotWidget.setPageTrackingConsent(false).catch(error => {
    status.textContent = error.message;
  });

  form.addEventListener("submit", async event => {
    event.preventDefault();
    if (submit.disabled || !form.reportValidity()) return;
    submit.disabled = true;
    message.readOnly = email.readOnly = true;
    try {
      await window.MatnBotWidget.ready();
      pending ??= {
        message: message.value.trim(),
        visitor: email.value.trim() ? { email: email.value.trim() } : undefined,
        clientRequestId: crypto.randomUUID()
      };
      await window.MatnBotWidget.startConversation(pending);
      status.textContent = "Message accepted / تم استلام الرسالة";
      pending = undefined;
      form.reset();
    } catch (error) {
      status.textContent = error.message + " (" + error.code + ")";
      if (!error.retryable) pending = undefined;
    } finally {
      // An uncertain network result keeps the same payload and request ID.
      // Click submit again to retry it; do not silently change its content.
      message.readOnly = email.readOnly = !!pending;
      submit.disabled = false;
    }
  });
</script>

What accepted means

The user message was saved. A bot or operator reply may arrive later. Clear form fields only after acceptance. Acceptance does not confirm that a person read the message.

Methods

MethodBehavior
ready()Waits for configuration and saved-session restoration. Call before other asynchronous commands.
open() / close()Opens or closes the floating panel. Closing the panel does not close the conversation.
startConversation(options)Reuses this tab's active conversation, applies supplied details, and sends the message. Resolves when the message is saved.
startNewConversation(options)Explicitly starts a separate conversation. Keeps the previous local view until the new session is confirmed.
setDraft(text)Fills the composer without sending. Up to 10,000 characters; the bot's sending limit may be lower.
getTags()Returns the current website tags and their version for an active conversation.
setTags(tags) / addTags(tags) / removeTags(tags)Replaces, adds or removes allowed website tags after server acceptance.
setPageTrackingConsent(consented, { preChat })Controls this widget instance's tracking. Pre-chat capture requires explicit opt-in.
trackPage({ url, title, isVisible })Reports a same-origin page. A queued result is not a persistence receipt.
on(event, callback)Subscribes to an event and returns an unsubscribe function.
destroy()Removes UI, listeners, timers and the live connection. Does not close or delete the server conversation.

Commands are serialized with up to eight pending actions. A message receipt can arrive while the bot is still replying; a new send can return WIDGET_QUEUE_FULL until the current stream ends.

Visitor & context

const receipt = await window.MatnBotWidget.startConversation({
  message: "I would like more information.",
  visitor: { name: "Example visitor", email: "visitor@example.com" },
  context: { source: "contact-form", formId: "contact", pagePath: location.pathname },
  tags: ["form:contact"], // First allow this tag in the bot's widget settings.
  clientRequestId: crypto.randomUUID() // Keep this ID and payload for retries.
});
// receipt.status === "accepted": the message was saved.
FieldRequirements
messageRequired; 1–10,000 characters, subject to the bot's configured limit.
visitor.name / email / phoneUp to 100 / 254 / 24 characters. Email and phone formats are validated; the bot can require fields.
context.source / formIdUp to 80 characters each: ASCII letters, digits, dot, underscore, colon or hyphen.
context.pagePathLocal path starting with /, up to 2,048 characters, without a query string or fragment.
tagsUp to 20 allowed website identifiers. Omission preserves existing tags; [] clears website tags when reusing a session.
clientRequestIdA UUID for this submission. Reuse it and the unchanged payload after an uncertain result.

Omitted contact fields remain unchanged on an existing session. Contact details are visitor-supplied, not verified identity. Context and tags do not become AI instructions or grant permissions. Separate profile/context/tag updates finish before sending; if a later update fails, an earlier successful update may remain.

Website tags

In Bot settings → Widget → Allowed website tags, add identifiers such as form:contact before using them. A bot can allow up to 100 identifiers; one conversation can have up to 20. Identifiers are lowercase and at most 40 characters, using letters, digits, dot, underscore, colon or hyphen and starting with a letter or digit.

await window.MatnBotWidget.addTags(["form:contact"]);
const { tags, version } = await window.MatnBotWidget.getTags();
await window.MatnBotWidget.removeTags(["form:contact"]);
// setTags([...]) replaces only the website tag set.

Website tags are separate from staff tags. Reserved namespaces include internal, security, staff, system, owner, priority and access. The public SDK does not reveal the bot's allowed-tag catalogue.

Page tracking & consent

Active-session page tracking defaults on when enabled in bot settings. If your site requires opt-in, call setPageTrackingConsent(false) immediately after loading the script, including on reload, before session restoration. Pre-chat history is off by default.

// Call immediately after loading the widget if your site requires opt-in.
await window.MatnBotWidget.setPageTrackingConsent(false);

// Call from your consent manager after the visitor chooses to share.
await window.MatnBotWidget.setPageTrackingConsent(true, { preChat: true });

// Optional for custom routers. Normal history navigation is observed automatically.
await window.MatnBotWidget.trackPage({ url: location.href });

// When consent is withdrawn:
await window.MatnBotWidget.setPageTrackingConsent(false);
  • Explicit pre-chat opt-in stores up to 20 visits in this tab's memory and uploads them only when a conversation starts. No identity is joined across tabs.
  • Initial routes, history changes and visible-page changes are tracked automatically. Hidden reports cannot replace the current visible page. An offline queue holds up to 100 visits.
  • The server keeps up to 100 visits per conversation for 30 days. Queries and fragments are removed; credential-bearing URLs are rejected. Configure /account/** or /customers/* to redact sensitive path values. Titles on redacted routes are omitted.
  • Opting out clears unsent visits and stops future tracking. It does not erase telemetry already accepted by the server. Authorized operators see the journey in conversation customer details.

Optional location

Location is optional. The SDK never opens a geolocation prompt itself. Ask only after a clear visitor action and let chat continue if they decline.

// After the visitor explicitly agrees to share an approximate area:
await window.MatnBotWidget.startConversation({
  message: "Please help me find a service in this area.",
  location: { area: "Amman", consentGranted: true },
  clientRequestId: crypto.randomUUID()
});

// Alternatively, pass coordinates from a visitor-initiated geolocation action:
// location: { latitude, longitude, accuracyMeters, consentGranted: true }
// If permission is denied, omit location and continue the conversation.

An area is at most 160 characters. Coordinates use WGS84 latitude −90…90 and longitude −180…180; optional accuracy is 0…50,000 meters. consentGranted: true is a client acknowledgement, not independent proof. Stored location expires after 30 days. The API does not calculate a nearest branch or travel time.

Events & cleanup

readyopenedclosedconversationStartedmessageAcceptederror
const unsubscribe = window.MatnBotWidget.on("messageAccepted", event => {
  // Update your form UI. event has status and clientRequestId, no message text.
  document.querySelector("#form-status").textContent = event.status;
});

// When the host component is removed:
unsubscribe();
// To remove the widget entirely:
window.MatnBotWidget.destroy();

SDK receipts, events and safe errors exclude message text, contact details, location and session tokens. Destroying the widget is local cleanup, not deletion of the conversation.

Errors & retries

Handle rejected promises using error.code, error.message and error.retryable. Keep the request ID and all submitted values unchanged after a timeout or lost response. Start receipts last 24 hours, but replay also requires a live session: widget access expires after 30 minutes without message activity or 24 hours from creation. Page visits and heartbeats do not extend that lifetime.

CodeNext step
WIDGET_ACCESS_DENIEDCheck the bot's allowed origins, pause switch and public-link policy in Widget settings. Do not repeatedly retry a denied request.
WIDGET_CHALLENGE_FAILED / WIDGET_CHALLENGE_UNAVAILABLE / WIDGET_SECURITY_UNAVAILABLEKeep the form. Let the visitor retry verification or wait for the service to recover. Never bypass verification.
WIDGET_LOAD_FAILED / WIDGET_RESTORE_FAILEDLoading or restoration failed. Keep form values and retry when connectivity returns.
VISITOR_*_REQUIRED / CHAT_VISITOR_EMAIL_INVALID / VISITOR_PHONE_INVALIDSupply the required fields or correct their format.
MESSAGE_INVALID / MESSAGE_TOO_LONGProvide a non-empty message within the bot's limit.
WIDGET_TAG_NOT_ALLOWED / WIDGET_TAG_LIMITUse only configured website tags, up to 20 per conversation.
WIDGET_TAG_VERSION_CONFLICTRead the latest tags before retrying a mutation. The SDK already attempts one refresh.
WIDGET_ORIGIN_INVALID / WIDGET_ORIGIN_MISMATCH / WIDGET_PAGE_INVALIDCheck the embedding origin and page URL. Do not reuse a session from another origin.
LOCATION_CONSENT_REQUIRED / WIDGET_LOCATION_INVALIDObtain consent and valid location data, or omit location and continue chat.
IDEMPOTENCY_KEY_REUSEDThis ID has different content attached to it. Retry the original submission unchanged.
SESSION_EXPIRED / WIDGET_START_RECEIPT_EXPIREDAsk the visitor to explicitly start a new conversation; do not silently duplicate the request.
RATE_LIMITED / NETWORK_UNAVAILABLE / WIDGET_QUEUE_FULLWait and retry if error.retryable is true, keeping the same request ID and payload.
REQUEST_FAILEDA safe fallback for an unrecognized failure. Keep the form and inspect retryable.

Security & privacy

Public documentation, protected conversations

Configure exact allowed HTTPS origins in Bot settings → Widget → Widget and website security. New bots start with enforcement and require setup. Existing bots migrate in monitor mode: unlisted origins are recorded but still allowed until an administrator enables enforcement. www and each subdomain must be listed separately.

For website-only use, disable the separate public chat link so it cannot become an alternative entry point. Origins can be forged by scripts outside the browser; use the shared rate limits and credit budgets, and optionally require Turnstile. The hosted embed handles the challenge without changing its script tag. Turnstile requires server keys, registered customer hostnames and compatible site CSP; verify these before enabling it. Challenge verification is never silently skipped during an outage.

Super Admin → Security & limits shows sampled widget security events and offers a temporary per-bot network block. Bot administrators can pause public chat. A shared network can include legitimate visitors, so review samples before blocking. These controls do not replace an edge firewall or protect a compromised embedding website.

The script and its network requests are visible to visitors. Keeping documentation private is not an access-control mechanism. Public examples expose no account data; conversation access still requires its session capability and server validation.

  • Use HTTPS. Never put server API keys in browser code. Let the SDK manage session credentials; do not copy them into URLs, logs, analytics or your form data.
  • Origin checks protect browser boundaries; they do not prove a caller is human. External clients can forge origin headers. Abuse resistance needs server-side limits, usage monitoring and additional bot protection when appropriate.
  • Automatic redaction cannot recognize every personal value. Configure sensitive route patterns, obtain consent where required and avoid exact addresses or sensitive information in page titles and paths.
  • Code running on your own page can access browser-held data. Keep your host site and third-party scripts secure. Widget link trusted-domain settings control clickable links; they are not an embedding access allowlist.

OWASP REST security guidance ↗