Widget Events
Listen in the host page
// Catch all
window.addEventListener('duki:event', (e) => {
console.log('[DUKI]', e.detail.name, e.detail);
});
// Specific
window.addEventListener('duki:chat:init', (e) => {
const { chatId } = e.detail;
});
Check the "Event catalog" to see events names
Payload
{
name, // normalized event name, e.g. "chat:init"
ts, // ISO timestamp
projectId, // widget project id
language, // e.g., "en"
side, // "right" | "left"
...custom // event-specific fields (chatId, message, error, ...)
}
Event catalog
| Event | When | Detail |
|---|---|---|
| Meta | ||
duki:event | Every event (catch-all) | name, ts, projectId, customFields, + payload |
| Panel & lifecycle | ||
duki:chat:open | Chat opens | source, widget_mode |
duki:chat:close | Chat closes | — |
duki:chat:fullscreen | Enter fullscreen | — |
duki:chat:smallscreen | Exit fullscreen | — |
duki:chat:closeRequested | User clicked Close in chat menu | chatId |
duki:chat:toggleExpand | Expand / collapse from chat menu | expanded, chatId |
| Session & messages | ||
duki:chat:init | Session ready | chatId |
duki:chat:start | Session started | chatId |
duki:chat:newSession | New session created (after end chat) | chatId |
duki:chat:ended | Chat ended | chatId |
duki:chat:history | History received | messages[] |
duki:chat:historyend | History rendered | messages[] |
duki:chat:typing | Typing on | — |
duki:chat:typingend | Typing off | — |
duki:chat:thinking | Thinking on | — |
duki:chat:thinkingend | Thinking off | — |
duki:chat:message | User sent a message | message |
duki:chat:newMessage | Bot message received (not history) | chatId |
duki:chat:error | Error | error |
| End chat | ||
duki:chat:endChatRequested | User requested end chat | chatId |
duki:chat:endChatPopupOpened | End chat rating popup opened | chatId |
duki:chat:endChatPopupClosed | End chat popup closed | chatId, reason |
duki:chat:endChatRatingSelected | Thumbs up / down selected | chatId, rating |
duki:chat:endChatFeedbackSent | Feedback submitted | chatId, rating, hasComment |
duki:chat:endChatDisconnected | WebSocket disconnected | chatId |
duki:chat:endChatStartNewRequested | Start new chat clicked | chatId |
| Lead form | ||
duki:chat:leadFormOpened | Lead form opened | chatId |
duki:chat:leadFormClosed | Lead form closed | chatId |
duki:chat:leadFormSubmitted | Lead form submitted | chatId, lead |
| Tooltip | ||
duki:tooltip:show | Chat icon tooltip shown | variant, source, textLength |
duki:tooltip:close | User dismissed tooltip | variant, reason, dismissedSaved |
| Page analytics (every 15s + on exit) | ||
duki:analytics:page_view | Page loaded with widget | url, visitorId |
duki:analytics:time_on_page | Every 15 seconds on page | timeOnPageSec, maxScrollPercent, scrollBucket |
duki:analytics:page_close | User leaves page / tab hidden | timeOnPageSec, maxScrollPercent |
duki:analytics:chat_engagement | First chat open on page | timeOnPageSec, engagementPageUrl |
| Custom fields | ||
duki:customFields:updated | Custom fields updated (e.g. disclaimer) | customFields |
duki:updateCustomFields | Same update, forwarded from iframe | customFields |
| Search mode | ||
duki:searchMode:expand | Search panel expanded | — |
duki:searchMode:close | Search panel closed | — |
GTM / GA4
<script>
window.dataLayer = window.dataLayer || [];
window.addEventListener('duki:event', (e) => {
const ev = e.detail;
window.dataLayer.push({
event: 'duki_' + ev.name.replace(/:/g, '_'),
duki: ev
});
});
</script>
This is an exmple how you can use the events.
AI Chat Custom Fields
Add contextual data to enrich conversations. Define window.dukiCustomFields before loading the chat widget.
These fields are automatically sent on duki:chat:start.
<script>
window.dukiCustomFields = {
// Business Context
country: "",
userMarket: "",
// Page Context
currentPageUrl: window.location.href,
currentPageTitle: document.title,
previousPageUrl: document.referrer,
// User Context
visitorName: "",
visitorEmail: "",
companyName: "",
userRole: "",
industry: "",
// Behavioral Context
pageCategory: "",
userIntent: "",
customerType: "",
// Technical Context
userAgent: navigator.userAgent,
language: navigator.language,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
// Session Context
sessionId: "",
visitCount: "",
lastVisit: "",
// tooltip text and auto open
tooltipText: "Your custom tooltip text here",
autoOpen: true // or false to disable auto-open
};
</script>
You can dynamically add values with:
<script>
window.dukiSetCustomField = function(key, value) {
if (!window.dukiCustomFields.dynamicFields)
window.dukiCustomFields.dynamicFields = {};
window.dukiCustomFields.dynamicFields[key] = value;
};
// Example usage
window.dukiSetCustomField("planSelected", "Pro");
window.dukiSetCustomField("cartValue", "199.90");
</script>
This helps personalize AI replies, analytics, and lead enrichment.