Team Signal Security chatbot — WordPress embed

One script tag and one init call. The bundle is self-contained: no jQuery, no CDN, no runtime dependency of any kind, and it renders inside a shadow root so the WordPress theme cannot style it and it cannot style the theme.

1. Upload the bundle

Upload ts-security-chatbot.js to the site (Media Library, or /wp-content/uploads/, or wherever it is hosted) and note its public URL.

2. Paste this — production

WordPress: Appearance → Theme File Editor → footer.php before </body>, or a header/footer-scripts plugin, or a Custom HTML block. It must load on every page the chat should appear on.

<script src="https://teamsignal.com.au/wp-content/uploads/ts-security-chatbot.js"></script>
<script>
  window.TSSecurityChat.init({
    webhookUrl: 'https://REPLACE-WITH-N8N-HOST/webhook/ts-security-chatbot-intake'
  });
</script>

3. Or this — demo mode (no webhook, nothing leaves the page)

Every effect goes to the browser console instead of a webhook. Use it for the client demo, for a staging page, and any time the real destination is not ready. webhookUrl is not required and is ignored.

<script src="./ts-security-chatbot.js"></script>
<script>
  window.TSSecurityChat.init({ demo: true, openOnMount: true });
</script>

Config options

OptionDefaultWhat it does
webhookUrl n8n intake endpoint. Required unless demo or transport is set. Must be an absolute http(s) URL; anything else shows the call-us fallback instead of a chat.
demofalse Console transport. Nothing is posted anywhere.
openOnMountfalse Open the panel immediately instead of waiting for the visitor to click the launcher.
messageDelayMs300 Pause between consecutive bot messages. 0 disables it. This is a pause, not a fake "typing" indicator.
containerdocument.body Element the widget host is appended to. Leave it alone unless the theme does something unusual with body.
storageKeyts-security-chatbot sessionStorage key. Change it only if two widgets ever share a page.
storagesessionStorage Pass null to disable session persistence entirely.
onErrorno-op function (stage, detail) {} — everything that went wrong, for the host page's own logging.
onEffectno-op function (effect) {} — every engine effect, transported or not. Observation only.

Other globals

window.TSSecurityChat.init(config)   // mounts; returns the controller
window.TSSecurityChat.destroy()      // removes the widget from the page
window.TSSecurityChat.instance()     // the live controller, or null
window.TSSecurityChat.version        // widget version, also sent in every envelope

init() never throws and never breaks the host page. Called twice it keeps the first widget and reports the duplicate through onError. If the config is unusable it renders a static "call us" panel with the phone number rather than a broken chat.

Removing it

Delete the two script tags. That is the whole rollback — the site returns to its current forms-only state, and no data migration is involved.