Widget Embed Guide
RobertsAutomotiveAIChat — v1.0.0
Paste this before the closing </body> tag. Replace YOUR_SITE_KEY with the key from your ClientSite record. The widget auto-initializes — no additional JavaScript needed.
<!-- Paste before </body> — widget launches automatically -->
<script src="https://roberts.blokbyblok.org/api/apps/__APP_ID__/functions/widgetLoader?siteKey=YOUR_SITE_KEY" async></script></body>, and the chat widget appears immediately.Load the script in <head>, then call init() at the bottom of your page body.
<!-- Option B: separate script + init call (more control) -->
<script src="https://roberts.blokbyblok.org/api/apps/__APP_ID__/functions/widgetLoader" async></script>
<script>
window.RobertsAutomotiveAIChat.init({
siteKey: "YOUR_SITE_KEY",
domain: window.location.hostname
});
</script>Mount the chat panel inside a specific container instead of a floating launcher. Size the container however you like — the widget fills it.
<!-- Inline / embedded mode — widget fills this container -->
<div id="dealer-ai-chat" style="width:420px; height:640px;"></div>
<script src="https://roberts.blokbyblok.org/api/apps/__APP_ID__/functions/widgetLoader?siteKey=YOUR_SITE_KEY" async></script>
<script>
window.RobertsAutomotiveAIChat.init({
siteKey: "YOUR_SITE_KEY",
container: "#dealer-ai-chat"
});
</script>window.RobertsAutomotiveAIChat.init({
siteKey: "YOUR_SITE_KEY", // required
domain: window.location.hostname, // recommended
position: "bottom-right", // "bottom-right" | "bottom-left"
openOnLoad: false, // open panel immediately
initialPrompt: null, // auto-send this text on open
container: null, // CSS selector → inline mode
debug: false, // console logging
theme: {
primaryColor: "#0066B1", // overrides brand color
},
pageContextOverrides: {}, // merge into auto-detected context
userContext: null, // { name, email, phone }
onError: function(err) { // optional error callback
console.error("Widget error:", err.code, err.message);
},
});var chat = window.RobertsAutomotiveAIChat;
// Subscribe to events
chat.on("ready", function() { console.log("Widget ready"); });
chat.on("opened", function() { /* panel visible */ });
chat.on("closed", function() { /* panel hidden */ });
chat.on("messageSent", function(data) {
// data.text, data.sessionId
myAnalytics.track("chat_message", { text: data.text });
});
chat.on("inventoryCardClicked", function(data) {
// data.vehicle = { id, year, make, model, price, ... }
window.dataLayer.push({ event: "vehicle_card_click", vehicle_id: data.vehicle.id });
});
chat.on("handoffRequested", function(data) {
// AI wants a human — route to CRM
fetch("/crm/escalation", { method: "POST", body: JSON.stringify(data) });
});
chat.on("error", function(err) {
// err.code = MISSING_SITE_KEY | BLOCKED_DOMAIN | CONFIG_NOT_FOUND | BACKEND_UNAVAILABLE
console.warn("Widget error:", err.code);
});Page context (URL, page type, visible vehicles) is detected automatically via pushState interception. Override manually for precise control.
// SPA navigation is detected automatically via pushState/popstate/polling.
// Override page context manually when needed:
router.afterEach(function(to) {
window.RobertsAutomotiveAIChat.setPageContext({
pageType: to.meta.pageType || "other",
currentBrand: to.query.brand || null,
});
});
// On VDP pages — pass current vehicle so AI has full context:
window.RobertsAutomotiveAIChat.setPageContext({
pageType: "vdp",
currentVDPVehicle: {
id: vehicle.id, make: vehicle.make,
model: vehicle.model, year: vehicle.year,
trim: vehicle.trim, price: vehicle.price,
stock_number: vehicle.stock,
}
});Every error surfaces via on("error", fn) and as a DOM CustomEvent. The widget never throws or breaks the host page.