Chat widget
Install it on your website, sign your users’ identity and control it with window.Domu.
Install it on your website
Copy the snippet from Settings › Web chat (widget) and paste it before the closing body tag. data-key is the widget’s public key; data-locale sets the language (es, en, pt); data-property and data-property-code tell which listing the person is writing from. Your domain must be in the widget’s allowed list.
<!-- Antes de cualquier Domu(…): crea la cola -->
<script>window.Domu=window.Domu||function(){(Domu.q=Domu.q||[]).push(arguments)};</script>
<script async charset="utf-8" src="https://app.bydomu.com/widget.js"
data-key="dom_XXXXXXXXXXXXXXXX"
data-api="https://api.bydomu.com"
data-locale="es"
data-property="ID_DEL_INMUEBLE"
data-property-code="P-0042"></script>The first line creates the queue: everything you call before the script loads is kept and runs in order once the widget has a session. If you ask to open it several times before that, the last one wins.
// Puede ir antes de que cargue widget.js: se ejecuta en orden cuando esté listo.
Domu('on', 'ready', ({ available }) => console.log('chat listo', available))
Domu('setContext', { page: location.pathname })
Domu('open', { view: 'chat', message: 'Hola' })Sign your users’ identity
If your users sign in on your website, sign them: their conversation follows them across devices, Noa treats them as verified and their attributes count as verified. Generate the secret in Settings › Web chat (widget) › Verified identity. userHash is the HMAC-SHA256 of the userId with that secret, in hex (without a userId, of the email). If it doesn’t match, the chat stays anonymous.
import { createHmac } from "node:crypto";
// En tu servidor: el secreto nunca va al navegador.
const userHash = createHmac("sha256", process.env.BYDOMU_WIDGET_SECRET)
.update(String(user.id))
.digest("hex");Domu('boot', {
userId: 'u_123', // el id de tu sistema
email: '[email protected]',
name: 'Ana Gómez',
userHash: '<<calculado en tu servidor>>',
})
// Al cerrar sesión en tu web:
Domu('shutdown')Open and control it
Open the widget on the view you need, with a message written or sent. “history” is the ongoing conversation. If the agency asks for details before starting, sending goes through that form first.
Domu('open') // abre donde corresponda
Domu('open', { view: 'help' }) // 'home' | 'chat' | 'help' | 'history'
Domu('open', { message: '¿Sigue disponible?' }) // deja el mensaje escrito
Domu('open', { view: 'chat', message: 'Quiero visitarlo', send: true }) // lo envía
Domu('close')
Domu('toggle')
Domu('showNewMessage', 'Hola, quiero información')
Domu('startConversation', { message: 'Quiero arrendar' }) // envía (send: true por defecto)
Domu('startConversation', { message: 'Borrador', send: false }) // solo escribe
Domu('showArticle', 'id-del-articulo')
Domu('showSearch', 'mascotas')Send it data and context
update stores the attributes on the contact (the agent sees them in the conversation). If nobody has written yet, they wait and move to the contact once it’s created. Name, email and phone only fill what’s missing, never replace data. setContext describes the current conversation (page, listing, cart): Noa and the agent see it. It merges by key.
Domu('update', {
name: 'Ana Gómez',
email: '[email protected]',
phone: '+57 300 000 0000',
attributes: { plan: 'pro', visitas: 3, vip: true, color: null }, // null borra
})
Domu('setContext', { page: '/inmuebles/p-0042', propertyCode: 'P-0042', cart: { items: 2 } })
Domu('setContext', { cart: null }) // quita una clave
Domu('setContext', null) // borra todoRecord and listen to events
track leaves a “Website event” activity on the contact record, with the page. on and off subscribe to ready, open, close, message and unreadCountChange. message fires for what the visitor sends and each new reply; ready is called even if you subscribe late. onUnreadCountChange keeps working.
Domu('track', 'viewed_listing', { code: 'P-0042', price: 2800000 })
function onMessage(m) {
// m = { id, from: 'visitor' | 'team', author: 'noa' | 'agent' | null, body, timestamp }
if (m.from === 'team') analytics.track('chat_reply')
}
Domu('on', 'message', onMessage)
Domu('on', 'open', () => {})
Domu('on', 'close', () => {})
Domu('on', 'unreadCountChange', (count) => updateBadge(count))
Domu('off', 'message', onMessage)Without writing code
Any element with these attributes opens the widget on click, including ones you add later:
<button data-domu-open>Chatear</button>
<button data-domu-open="help">Centro de ayuda</button>
<a href="/contacto" data-domu-message="¿Está disponible el P-0042?">Preguntar</a>
<button data-domu-message="Quiero agendar una visita" data-domu-send>Agendar visita</button>
<button data-domu-article="id-del-articulo">Requisitos para arrendar</button>Limits and security
20 / 50^[A-Za-z][\w.-]{0,39}$text ≤ 255, number, boolean or null≤ 20 keys and 2,000 characters100 per session · name ≤ 60 · 10 props60 per session per minute · body ≤ 8 KBEverything the page sends is “unverified” unless the session has a valid userHash, and an unsigned attribute never overwrites a verified one. Noa receives it as data, never as instructions. The API only writes to the visitor’s session and contact: there’s no way to read other visitors’ data. Invalid keys are dropped with a console warning.