window.ProbotSelfHosted.
What you’ll build
- A static HTML page that mounts the widget via
ProbotSelfHosted.mount(el, config). - Any backend (Node, Python, Go, Rust, PHP, Cloud Function - whatever) that
handles
POST /api/probot-chat. The widget doesn’t care what language answers the request; it only needs{ reply: "…" }JSON back.
1. HTML markup
mount() takes either a CSS selector string or a real DOM element as its
first argument. It returns the React root so you can call .unmount()
later if you need to.
2. Backend - any language
The widget callsPOST /api/probot-chat with:
Node (Express)
Python (FastAPI)
Cloudflare Worker
PHP
Pinning versions
Public CDNs can move fast. Pin the version in the script URL so a future release can’t silently change your widget:integrity hash with curl -s <url> | openssl dgst -sha384 -binary | openssl base64 -A.
Local development
Set up any static-file server plus your backend of choice. Two easy options:fetch("/api/probot-chat") will hit whatever origin serves
the HTML - point your reverse proxy (nginx, Caddy) or the backend framework
itself to answer that route.
Same-origin vs CORS
The example above assumes the HTML page and the/api/probot-chat route
are served from the same origin. If they’re on different origins (e.g. the
static page is on example.com and the API is on api.example.com),
either:
- Enable CORS on the backend so it accepts requests from the HTML’s origin, or
- Front both with a reverse proxy that presents one origin to the browser.
What the widget renders
The IIFE build is entirely self-contained: all styles are injected into the page’s<head> at mount time. That means:
- No CSS file to load.
- No conflict with your existing styles (all class names are prefixed
probot-*). - The FAB is
position: fixed; bottom: 20px; right: 20px;by default. To reposition, override.probot-rootin your page’s CSS after the script mounts.
Security notes
dashboard.tokenvisibility. The token is inline in your page source, so anyone viewing source can copy it. That’s OK - it only grants conversation + lead writes for one bot, and you can revoke it from the dashboard in one click.OPENAI_API_KEYvisibility. The LLM API key must NEVER appear in the HTML. Keep it in the backend’s environment variables only. If you see the key in DevTools’ Sources tab, stop and move it to the server.- Rate limiting. A publicly-embedded widget = anyone can drain your LLM credits. Add per-IP rate limiting on the backend (see Next.js example § rate limiting for a template).