Widget B2C — visitante invitado y empresa
Esta sección cubre las integraciones en las que el widget permanece en sesión de invitado:
- sitio público, visitante desconocido: solo el script y el formulario de contacto
- sitio o portal que ya conoce la cuenta:
init()con perfil y datos de empresa, formulario omitido cuando los datos bastan, contacto vinculado a la empresa
El historial personal en otro navegador o dispositivo es otra integración. Ve Widget B2B. Contrato completo de los campos, cambio de cuenta en una SPA, CSP y provisioning: Integración técnica.
Asociar una empresa no autoriza el historial de otra persona. Un usuario autenticado en el sitio sigue siendo un invitado del widget hasta que envíes external_user_id junto con un restore_id válido.
Qué obtienes
- Un botón flotante, o un chat incrustado en un contenedor de la página
- Un formulario inicial para quien es desconocido, con las etiquetas Teléfono y Nombre. En
init()los campos sonphoneyname - Una sesión de invitado guardada por el loader en el mismo navegador, separada por clave de widget. Si
localStorageestá bloqueado, la continuidad puede quedarse en la página actual - Si envías
external_id, Quoting24 busca una empresa con el mismoweb_code. Concompany_nameademás, puede crear la empresa que falta y vincular el contacto - Tickets y respuestas en Quoting24, como en los demás canales
La sesión de invitado relee su conversación más reciente incluso después de cerrar la pestaña. No sigue a la persona a otro navegador.
1. Requisitos previos en Quoting24
- Canal Widget activo en Configuración → Canales (Impostazioni → Canali)
- Logotipo, color, posición, saludo, bienvenida y fuera de horario configurados
- Dominios permitidos actualizados, incluidas las pruebas y
localhost - Código de inserción copiado de la configuración del canal: URL del script y clave pública
Usa el host y la clave del mismo entorno. En los ejemplos sustituye HOST_WIDGET y CLAVE_PUBLICA_WIDGET.
Los dominios son nombres de host, sin https://, puerto, ruta ni *.. example.com incluye los subdominios. Lista vacía: ninguna restricción. En local sirve la página por HTTP, no como file://.
El company_name de init() es la razón social del cliente. No es el título del widget y no se configura desde este payload.
2. Instalar el botón flotante
Pega el script una sola vez, antes de </body>:
El botón aparece después de cargar los ajustes gráficos. Un visitante nuevo ve el formulario de contacto. Quien vuelve en el mismo navegador retoma la sesión de invitado, si el almacenamiento está disponible.
El script expone un único window.Quoting24Widget.
3. Incrustar el chat en la página
Crea el contenedor antes del script y dale una altura explícita:
<section aria-labelledby="titulo-asistencia">
<h2 id="titulo-asistencia">Asistencia</h2>
<div id="chat-asistencia" style="width: 100%; height: 600px;"></div>
</section>
<script
src="https://HOST_WIDGET/assets/widget/script.js?key=CLAVE_PUBLICA_WIDGET&mode=embedded&target=chat-asistencia"
async
></script>
targetes el ID del elemento, sin#- El iframe llena el contenedor
- No hay botón flotante
open(),close()ytoggle()no tienen efecto- En este modo el sitio decide altura y anchura. En modo flotante, en el móvil, el chat puede ocupar el viewport
El modo incrustado, por sí solo, no habilita la restauración del historial.
4. Enviar el perfil del cliente
Cuando el sitio conoce la cuenta, envía el perfil después de cargar el loader. Los campos son cadenas. Omite los desconocidos: no envíes null, IDs numéricos ni un correo vacío.
window.Quoting24Widget.init({
external_id: "52",
company_name: "Ricambi Stella S.r.l.",
name: "mario.rossi",
phone: "+393331234567",
email: "amministrazione@ricambi-stella.example",
});
| Dato en el sitio o en el ERP | Campo | Significado |
|---|---|---|
Referencia estable de la empresa, el mismo valor importado en web_code |
external_id |
Comparación exacta con companies.web_code, en la organización del widget |
| Razón social | company_name |
Se usa si hay que crear la empresa |
| Nombre de la persona o del usuario | name |
Nombre mostrado en el contacto |
| Correo de la cuenta | email |
Se guarda en la empresa solo cuando la empresa se crea |
| Teléfono, si lo conoces | phone |
Número del contacto. Prefiere el formato internacional, por ejemplo +393331234567 |
Este payload no envía external_user_id ni restore_id. Mantén separados name y company_name: no concatenes la razón social al nombre de usuario para obtener la asociación. El campo público es external_id. No envíes web_code, el UUID interno de Quoting24 ni companies.code.
El formulario muestra Nombre y Teléfono. En init() esos campos se llaman name y phone. El correo se llama email. Nombre, Teléfono y Email no son claves: el loader las descarta, igual que descarta web_code en lugar de external_id.
Ejemplos
Nombre y teléfono, los mismos datos del formulario. El chat empieza y no busca una empresa.
Perfil con empresa. name es la persona, phone el número del contacto y email se escribe en la empresa solo si esta llamada la crea.
widget.init({
external_id: "52",
company_name: "Ricambi Stella S.r.l.",
name: "Mario Rossi",
phone: "+393331234567",
email: "mario.rossi@ricambi-stella.example",
});
Solo correo. Una dirección válida en email basta para abrir la sesión.
Si un campo supera los límites o el correo está mal formado, la petición se rechaza. Límites y comportamiento de cada campo: Integración técnica.
Esperar al loader
Un script async puede no haberse ejecutado todavía cuando el código posterior llama a init(). Para datos precargados usa un módulo de carga en lugar de la etiqueta estática, importado una sola vez en el layout principal:
// /assets/quoting24-loader.js
const widgetScriptUrl =
"https://HOST_WIDGET/assets/widget/script.js?key=CLAVE_PUBLICA_WIDGET";
let widgetPromise;
export function loadWidget() {
if (window.Quoting24Widget) {
return Promise.resolve(window.Quoting24Widget);
}
if (widgetPromise) return widgetPromise;
widgetPromise = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.id = "quoting24-widget-script";
script.src = widgetScriptUrl;
script.async = true;
script.onload = () => {
if (window.Quoting24Widget) resolve(window.Quoting24Widget);
else {
script.remove();
reject(new Error("La API del widget no está disponible"));
}
};
script.onerror = () => {
script.remove();
reject(new Error("No se pudo cargar el widget"));
};
document.body.appendChild(script);
}).catch((error) => {
widgetPromise = undefined;
throw error;
});
return widgetPromise;
}
import { loadWidget } from "/assets/quoting24-loader.js";
const widget = await loadWidget();
widget.init({
external_id: "52",
company_name: "Ricambi Stella S.r.l.",
name: "mario.rossi",
phone: "+393331234567",
email: "amministrazione@ricambi-stella.example",
});
Importa el módulo desde el bundler, o carga el archivo de entrada con <script type="module" src="/assets/asistencia.js"></script>. Para el modo incrustado añade &mode=embedded&target=chat-asistencia a la URL y monta antes el contenedor.
loadWidget() solo indica que la API JavaScript está disponible. init() devuelve void: encola el perfil si el iframe no está listo y no espera a la sesión. Para el resultado usa isReady() y hasSessionError().
init() sustituye el perfil guardado. No lo fusiona con el anterior. Pasa todos los campos que deban permanecer.
Un cambio de página en una SPA no exige un script nuevo ni un reset(). Al cambiar de persona o de cuenta, y al cerrar sesión, llama a reset() antes del perfil nuevo: cambiar solo los campos no desvincula la sesión de invitado anterior. Patrón completo: Login, logout y cambio de cuenta.
5. Cómo se asocia o se crea la empresa
Quoting24 ejecuta este flujo en la inicialización, aunque el visitante aún no haya enviado un mensaje:
- Con un
external_idno vacío, busca una empresa de la misma organización cuyoweb_codesea igual a ese valor - Si la encuentra, vincula el contacto. El nombre y el correo de la empresa existente no cambian
- Si no la encuentra y también hay un
company_nameno vacío, crea la empresa conname = company_name,web_code = external_idy el correo si existe, y luego vincula el contacto - Si no puede encontrar ni crear la empresa, continúa con el perfil disponible. Si no basta para un invitado nuevo, muestra el formulario. Los errores de base de datos o el límite de peticiones hacen fallar la inicialización
| Datos enviados | Situación en Quoting24 | Resultado |
|---|---|---|
external_id: "52", company_name: "Ricambi Stella S.r.l." |
Existe web_code = "52" |
Usa la empresa existente y no cambia su nombre |
| Los mismos datos, más un correo válido | No existe web_code = "52" |
Crea la empresa con código web 52, razón social y correo, y asocia el contacto |
external_id: "52", name: "mario.rossi" |
Ninguna coincidencia | Inicia al invitado sin una asociación de empresa nueva |
external_id: "52", sin otros datos |
Existe una coincidencia | Inicia al invitado asociado a la empresa, con el nombre predeterminado Visitatore |
external_id: "52", sin otros datos |
Ninguna coincidencia | Muestra el formulario de contacto |
company_name sin external_id, más name |
Cualquiera | Inicia al visitante. No busca ni crea una empresa |
La búsqueda no usa razón social, correo, teléfono, NIF ni companies.code. "052" y "52" son valores distintos; los espacios de external_id no se quitan. Si importas el fichero antes, escribe en web_code el mismo valor que el sitio enviará en external_id. Ve también Empresas → Código web.
Un invitado nuevo arranca si hay al menos un nombre, un teléfono, un correo válido o un external_user_id, o una empresa encontrada o creada mediante external_id. init({}) y company_name solo no inician una sesión.
Para un contacto ya vinculado, un código válido nuevo puede cambiar su empresa. Omitir el código, o enviar uno que no se resuelve, no borra la asociación anterior. El cambio de persona o de cuenta exige reset().
Límites
web_code no es único. Con varias coincidencias, la inicialización pública toma la primera por fecha de creación ascendente. Peticiones simultáneas que aún no encuentran la empresa pueden crear más de una: la creación no es atómica y no sustituye una importación o una sincronización desde el ERP.
La creación implícita está limitada a 20 intentos por ventana de 60 segundos y dirección IP, en el proceso de backend que atiende la petición. Por encima del límite la inicialización falla con TOO_MANY_REQUESTS: reintenta con espera, sin bucles continuos de init().
Los datos salen del navegador y el visitante puede alterarlos. El vínculo no prueba la pertenencia a la empresa y no concede el historial de la empresa. Envía company_name solo si quieres permitir esta creación pública. Para actualizar empresas ya existentes usa las herramientas de Quoting24 o una integración de ERP autorizada.
El correo de la inicialización pública solo se guarda en la empresa nueva. No se escribe en el contacto y no actualiza una empresa ya existente. Un teléfono ya usado por otro contacto no une contactos ni chats.
6. Comportamiento en el sitio
| Situación | Comportamiento esperado |
|---|---|
| Primer visitante, solo script | Botón, luego formulario, luego chat |
init() con perfil suficiente |
Formulario omitido |
external_id ya presente como web_code |
Contacto asociado; nombre y correo de la empresa sin cambios |
external_id nuevo con company_name |
Empresa creada y contacto asociado |
Código nuevo y nombre, sin company_name |
Chat sin empresa nueva |
| Solo código nuevo | Formulario de contacto |
| Solo razón social y nombre | Ninguna búsqueda ni creación por nombre |
| Recarga en el mismo navegador | Sesión de invitado reanudada, si el almacenamiento está disponible |
| Otro navegador o dispositivo | Sesión de invitado nueva |
| Fuera de horario | Banner fuera de horario; la redacción sigue utilizable |
| Móvil, botón flotante | El chat puede ocupar el viewport |
Adjuntos: PDF, DOC, DOCX, JPG/JPEG, PNG. Máximo 10 MiB por archivo y 100 MiB de adjuntos referenciados por conversación.
7. Controles útiles
Cuando el botón flotante es visible puedes enlazar un botón del sitio a window.Quoting24Widget?.open().
| Método | Uso |
|---|---|
init(profile) |
Define o sustituye el perfil actual |
open() / close() / toggle() |
Solo widget flotante. Cerrar no es un logout |
isReady() |
Sesión de visitante iniciada. Sigue en falso en el formulario de contacto |
hasSessionError() |
Error de sesión señalado por el iframe |
reset() |
Borra perfil, sesión local, credencial de invitado guardada y no leídos de esta instancia |
dispose() |
Desmonta la interfaz y los listeners. No borra la credencial de invitado guardada ni el objeto API global |
No esperes a isReady() para que un invitado nuevo abra el formulario. No hay un evento público de disponibilidad. Estos indicadores no describen todo el estado de red.
Lista de comprobación antes de la puesta en marcha
- [ ] Host y clave del mismo entorno
- [ ] Dominios del sitio y del staging en la lista, solo como nombres de host
- [ ] Logotipo, color, saludo, bienvenida y fuera de horario comprobados
- [ ] Navegador nuevo, solo script: formulario, mensaje, ticket en Quoting24
- [ ]
external_idya enweb_code: asociación, ficha de empresa sin cambios - [ ] Código nuevo con
company_name: empresa creada con ese código web - [ ] Código nuevo sin
company_name: ninguna empresa creada - [ ] Correo vacío, ID numérico o razón social demasiado larga: payload rechazado, hay que corregirlo
- [ ] Recarga y navegación SPA: una sola instancia, sesión de invitado conservada
- [ ] Cambio de cuenta:
reset()de inmediato, respuestas tardías descartadas - [ ] Móvil: redacción utilizable
- [ ] Dominio no autorizado: widget rechazado
Problemas frecuentes: Resolución de problemas.