Widget web chat Copera Omni: setup, SDK e identità
Il widget web Copera Omni aggiunge la web chat al tuo sito e instrada ogni conversazione in un channel Omni — una inbox condivisa che può ricevere conversazioni da più source contemporaneamente, come uno o più web widget e numeri WhatsApp, così il team lavora su tutte in un unico posto.
Questa guida ti porta da un'installazione di due minuti a un'integrazione production-grade: l'embed copy-paste, la command API CoperaOmni per launcher custom, la sicurezza browser-origin e il flusso sicuro per identificare i visitatori autenticati senza mai esporre una server key nel browser.
Source gratuite vs a pagamento
Un channel Omni può combinare diverse source. Il web widget è quella gratuita; WhatsApp è un add-on a pagamento.
Aggiungi tutte le source web-widget di cui hai bisogno senza costi. Tutto in questa guida usa il web widget gratuito.
Collega numeri WhatsApp allo stesso channel Omni come add-on a pagamento, così le conversazioni web e WhatsApp finiscono in un'unica inbox.
Rimuovere una source WhatsApp o eliminare un channel Omni non annulla lo slot del numero acquistato. Per smettere di pagare uno slot, riduci la quantità acquistata separatamente in Workspace Billing.
Quick start
Due minuti per un widget live.
In Copera, apri (o crea) un channel Omni, vai su Settings → Add source e scegli Web widget — Copera etichetta questa source come Free. Poi copia il channelKey mostrato sotto Configure & install.
Scegli Quick embed o JavaScript SDK sotto, incollalo nella pagina e sostituisci YOUR_CHANNEL_KEY con la key che hai copiato.
Il floating launcher di Copera compare nella posizione che hai configurato. Aprilo per avviare una conversazione.
- Quick embed
- JavaScript SDK
Posiziona questo snippet completo una sola volta, subito prima del tag di chiusura </body>. Carica il runtime CDN in modo asincrono, inizializza la source e mostra il floating launcher di Copera.
<script>
(function (w, d) {
w.CoperaOmni = w.CoperaOmni || function () {
(w.CoperaOmni.q = w.CoperaOmni.q || []).push(arguments);
};
var s = d.createElement('script');
s.async = 1;
s.src = 'https://widget.copera.ai/widget.js';
d.head.appendChild(s);
})(window, document);
CoperaOmni('init', { channelKey: 'YOUR_CHANNEL_KEY' });
</script>
Questo è tutto ciò di cui un sito HTML semplice ha bisogno. Per controllare il panel dal tuo bottone invece del floating launcher, passa alla tab JavaScript SDK.
Carica lo stesso runtime CDN, inizializzalo con launcher: 'custom' per sopprimere il floating launcher di Copera e guida il panel dal tuo controllo con la CoperaOmni command API.
<button id="support-chat" type="button" aria-controls="copera-support" aria-expanded="false">
Chat with support
</button>
<script>
window.CoperaOmni = window.CoperaOmni || function () {
(window.CoperaOmni.q = window.CoperaOmni.q || []).push(arguments);
};
</script>
<script async src="https://widget.copera.ai/widget.js"></script>
<script>
const launcherButton = document.querySelector('#support-chat');
CoperaOmni('on', 'open', function () {
launcherButton.setAttribute('aria-expanded', 'true');
});
CoperaOmni('on', 'close', function () {
launcherButton.setAttribute('aria-expanded', 'false');
});
CoperaOmni('init', {
channelKey: 'YOUR_CHANNEL_KEY',
launcher: 'custom',
locale: document.documentElement.lang || 'en',
});
launcherButton.addEventListener('click', function () {
CoperaOmni('toggle');
});
</script>
Usa open e close quando l'app sa già lo stato desiderato, e toggle per un singolo bottone launcher. Se hai mantenuto il launcher di default, setLauncherVisible(false) lo nasconde temporaneamente e setLauncherVisible(true) lo ripristina.
@copera/omni-widget-sdk è un wrapper tipizzato opzionale intorno a questi stessi comandi. Vedi Wrapper tipizzato e runtime CDN.
Il channelKey generato è la chiave pubblica di questa source web-widget specifica. È sicuro metterlo nel codice browser. Non è l'Omni channel ID o source ID, e non deve essere sostituito con nessuno dei due.
Se selezioni Regenerate key, il vecchio channelKey smette di funzionare. Sostituiscilo ovunque il widget sia incorporato.
Configurare il widget
Il quick start usa i default. Apri Settings (o Configure & install) della source widget per personalizzare l'esperienza completa prima di andare live.
Crea un channel usando il tipo Omni, oppure aprine uno esistente. Un singolo channel Omni può ricevere conversazioni da più source.
Apri le Settings del channel, seleziona Add source, poi scegli Web widget. Copera etichetta questa source come Free.
Inserisci un Display name, scegli Primary color e Launcher position, e opzionalmente imposta un Greeting message. Puoi anche abilitare Show a pre-chat form per i visitatori anonimi.
Per una nuova integrazione, scegli Browser origin (recommended) sotto Embed security. Aggiungi ogni origin esatta del sito che può incorporare il widget ad Allowed origins, premendo Enter dopo ciascuna — ad esempio, https://www.example.com e https://app.example.com.
Lascia Require verified visitor identity spento per consentire conversazioni anonime o pre-chat. Attivalo quando il sito chiamerà sempre identify con un identity token emesso dal server. Il pre-chat form e l'identità verificata obbligatoria non possono essere abilitati insieme.
Seleziona Create widget. Nella schermata di successo, copia Quick embed per il launcher integrato oppure seleziona JavaScript SDK per un launcher custom. Puoi tornare alla source e selezionare Configure & install in seguito.
| Setting | What it controls | Default |
|---|---|---|
| Display name | The name shown at the top of the chat panel. | Source name |
| Primary color | The accent color of the launcher and panel. | Workspace accent |
| Launcher position | Which corner the floating launcher sits in. | Bottom-right |
| Greeting message | An optional first message shown when the panel opens. | None |
| Show a pre-chat form | Asks anonymous visitors for details before the conversation begins. | Off |
| Embed security | How Copera decides which pages may embed the widget. | Browser origin |
| Require verified visitor identity | Blocks anonymous and pre-chat chats; requires a server-issued identity token. | Off |
JavaScript command API
https://widget.copera.ai/widget.js installa la funzione di comando globale CoperaOmni(command, ...args). Le chiamate fatte tramite lo snippet di coda prima che il runtime finisca di caricarsi vengono bufferizzate e riprodotti in ordine.
Comandi
| Command | Signature | Purpose |
|---|---|---|
init | CoperaOmni('init', config) | Initialize the widget. config.channelKey is required; locale and launcher are optional. |
identify | CoperaOmni('identify', identity) | Set the visitor identity. Prefer { identityToken } for signed-in users; { name, email } supports unverified visitor details. |
logout | CoperaOmni('logout') | Clear the current identity before a user signs out or another user takes over the page. |
destroy | CoperaOmni('destroy') | Remove the iframe, launcher, timers, listeners, and in-memory credentials. |
open | CoperaOmni('open') | Open the chat panel. |
close | CoperaOmni('close') | Close the chat panel. |
toggle | CoperaOmni('toggle') | Toggle the panel between open and closed. |
setLauncherVisible | CoperaOmni('setLauncherVisible', visible) | Show or hide the built-in launcher without destroying the widget. |
update | CoperaOmni('update', { locale }) | Change the live widget locale. |
on | CoperaOmni('on', eventName, callback) | Subscribe to an event. The live runtime returns an unsubscribe function. |
off | CoperaOmni('off', eventName, callback) | Remove the same callback previously passed to on. |
Le opzioni init supportate sono:
| Option | Type | Default | Description |
|---|---|---|---|
channelKey | string | Required | Public source key copied from Configure & install. |
locale | string | "en" | BCP 47 locale for the widget UI, such as "en" or "pt-BR". |
launcher | "default" | "custom" | "default" | Use "custom" to suppress Copera's launcher and control the panel yourself. |
Eventi
Ogni callback di evento riceve un oggetto il cui name identifica l'evento.
| Event | Payload | When it fires |
|---|---|---|
ready | { name: 'ready' } | The iframe is ready and has dispatched its initial state. This does not prove that backend initialization succeeded. |
open | { name: 'open' } | The panel opens. |
close | { name: 'close' } | The panel closes. |
identityRequired | { name: 'identityRequired' } | A closed conversation is being restarted on a source that requires the visitor to be verified again. |
unreadCount | { name: 'unreadCount', count: number } | The unread message count changes. |
conversationStarted | { name: 'conversationStarted', conversationId: string } | A conversation starts and its ID becomes available. |
Aggiorna un badge custom e registra l'inizio di una conversazione:
const unreadBadge = document.querySelector('#support-unread');
function handleUnread(event) {
unreadBadge.textContent = String(event.count);
unreadBadge.hidden = event.count === 0;
}
function handleConversationStarted(event) {
console.log('Omni conversation started', event.conversationId);
}
CoperaOmni('on', 'unreadCount', handleUnread);
CoperaOmni('on', 'conversationStarted', handleConversationStarted);
// When this page component is removed:
CoperaOmni('off', 'unreadCount', handleUnread);
CoperaOmni('off', 'conversationStarted', handleConversationStarted);
Tieni il riferimento al callback se intendi chiamare off; una nuova funzione inline non è lo stesso listener.
Usa gli eventi di lifecycle per tenere sincronizzata la UI host:
CoperaOmni('on', 'ready', function () {
document.querySelector('#support-chat').disabled = false;
});
CoperaOmni('on', 'open', function () {
document.querySelector('#support-chat').setAttribute('aria-expanded', 'true');
});
CoperaOmni('on', 'close', function () {
document.querySelector('#support-chat').setAttribute('aria-expanded', 'false');
});
CoperaOmni('on', 'identityRequired', function () {
// Ask the signed-in host application to refresh its identity token.
void identifyCurrentUser();
});
Identità del visitatore
Scegli quanto sai di ciascun visitatore. I visitatori anonimi e pre-chat non richiedono backend; i visitatori verificati usano un token a breve durata emesso dal server.
- Anonimo
- Pre-chat form
- Verificato
Lascia Require verified visitor identity spento. Un visitatore può iniziare senza dati di identità. Copera mantiene la widget session del browser associata alla conversazione.
Abilita Show a pre-chat form per chiedere al visitatore dettagli prima che la conversazione inizi. Le informazioni inserite dal visitatore sono contesto utile, ma non sono prova che il visitatore possieda quell'identità.
Per un cliente autenticato, il backend same-origin scambia la sua server API key legata alla source con un identity token a breve durata. Il browser passa solo quel token a identify. Abilita Require verified visitor identity quando le conversazioni anonime e pre-chat devono essere bloccate.
Chiamare identify con { name, email } fornisce dettagli visitatore non verificati. Per l'identità di account autenticato, usa invece { identityToken } — descritto di seguito.
Identità verificata sicura
L'identità verificata ha tre confini:
- Copera emette una server API key legata alla source con prefisso
cp_omni_sk_. - Il backend invia a Copera un identificatore cliente stabile e riceve un identity token di cinque minuti.
- Il browser riceve solo il token a breve durata e lo passa al widget.
La key cp_omni_sk_… va solo nello store di secret del backend. Non metterla mai in HTML, JavaScript browser, un'app mobile, log, proprietà analytics o un repository pubblico. Il channelKey pubblico e la server API key privata servono scopi diversi e non sono intercambiabili.
Apri il pannello Configure & install della source widget. Sotto Server API keys, seleziona Create server key, nominala per un backend o ambiente e copiala immediatamente — viene mostrata una sola volta. Sono supportate più key attive, il che consente la rotazione senza condividere una credenziale tra ambienti.
Memorizzala come variabile d'ambiente solo-backend, ad esempio COPERA_OMNI_SERVER_KEY.
Il backend chiama l'endpoint identity-token con la server key:
POST https://api.copera.ai/public/v1/omni-channel/identity-tokens
Authorization: Bearer cp_omni_sk_...
Content-Type: application/json
Il body della request accetta:
| Field | Required | Description |
|---|---|---|
externalId | Yes | An opaque, stable, non-PII identifier for the signed-in customer, scoped to your application. Use the same value on future visits. |
name | No | Current display name, from your trusted account record. |
email | No | Current email address, from your trusted account record. |
La server key lega già la richiesta a un workspace e a una source web-widget; non inviare un workspace ID, source ID, channel ID o channelKey in questa request. Vedi il riferimento API Create Omni identity token per lo schema e le response esatti.
La route Node.js seguente illustra il pattern same-origin. Assume che l'applicazione abbia già autenticato la request e reso l'utente autenticato disponibile come request.user.
export async function postOmniIdentity(request, response) {
const user = request.user;
if (!user) {
return response.status(401).json({ message: 'Sign in required' });
}
const coperaResponse = await fetch(
'https://api.copera.ai/public/v1/omni-channel/identity-tokens',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.COPERA_OMNI_SERVER_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
externalId: user.omniExternalId,
name: user.name,
email: user.email,
}),
},
);
if (!coperaResponse.ok) {
return response.status(502).json({ message: 'Unable to start support chat' });
}
const { identityToken, expiresAt } = await coperaResponse.json();
response.setHeader('Cache-Control', 'no-store');
return response.json({ identityToken, expiresAt });
}
Per lo stesso externalId stabile entro quella source widget, Copera crea il contact la prima volta e riusa lo stesso contact nelle sessioni verificate successive. Usa un valore opaco creato per l'applicazione. Non usare un display name, un indirizzo email, un numero di telefono, uno username, un database ID sequenziale o un altro valore direttamente identificante o mutabile.
Espone la route sulla stessa origin dell'applicazione, richiedi la sessione autenticata normale dell'applicazione e le protezioni CSRF, e restituisci solo il token a breve durata:
async function identifyCurrentUser() {
const response = await fetch('/api/omni-identity', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': window.appCsrfToken,
},
});
if (!response.ok) {
throw new Error(`Omni identity request failed: ${response.status}`);
}
const { identityToken } = await response.json();
await CoperaOmni('identify', { identityToken });
}
CoperaOmni('init', { channelKey: 'YOUR_CHANNEL_KEY' });
void identifyCurrentUser().catch(function (error) {
console.error('Unable to identify the Omni visitor', error);
});
Gli identity token scadono cinque minuti dopo l'emissione. Emittili on demand e usali immediatamente; non persistirli in local storage né trattarli come un login token long-lived.
Chiama e attendi CoperaOmni('logout') prima di identificare un utente autenticato diverso nella stessa pagina browser. Al sign-out dell'applicazione, chiama logout anche se intendi mantenere disponibile la chat anonima. Questo evita che l'identità di un cliente si trascini nella sessione di un altro.
async function switchOmniUser(nextUserIsSignedIn) {
await CoperaOmni('logout');
if (nextUserIsSignedIn) {
await identifyCurrentUser();
}
}
Quando un visitatore riavvia una conversazione chiusa e Require verified visitor identity è abilitato, il widget emette identityRequired così l'host può verificare di nuovo il visitatore. Richiedi un token fresco e chiama identify; non fare fallback a name e email forniti dal browser come se fossero verificati. Per la visita autenticata iniziale, identifica il visitatore in modo proattivo dopo init come mostrato sopra.
Sicurezza browser-origin
Browser origin (recommended) controlla la host page di embedding rispetto alle Allowed origins della source e stabilisce una embed session origin-bound a breve durata. Aggiungi l'origin della pagina che ospita il widget, come https://support.example.com — non https://widget.copera.ai. Configura origin esatte, incluso schema e porta quando presenti:
| Page URL | Allowed origin |
|---|---|
https://www.example.com/pricing | https://www.example.com |
https://app.example.com/support | https://app.example.com |
http://localhost:3000/account | http://localhost:3000 |
Path, query, fragment, username e password non fanno parte di un'origin. Aggiungi ciascun subdomain separatamente. Preferisci HTTPS fuori dallo sviluppo locale.
Scegli Browser origin (recommended) per i nuovi deployment e configura esplicitamente ogni origin consentita. Quando aggiungi o rimuovi un dominio di produzione, aggiorna Allowed origins prima di deployare l'embed lì. Rimuovere un'origin impedisce nuove embed session da quell'origin.
Rotazione e revoca
Tratta la public embed key e le server API key come credenziali separate:
| Action | Immediate effect | Follow-up |
|---|---|---|
| Regenerate key | Existing snippets using the old channelKey stop working. | Replace the key in every embed. |
| Revoke a server API key | Token-minting requests using that cp_omni_sk_… key fail immediately. | Deploy a replacement key first if the integration must remain available. |
Per una rotazione server-key a zero-downtime, crea una seconda key, deployala sul backend, conferma che il valore Last used si aggiorni, poi revoca la vecchia key. Usa key separate per production, staging e ciascun backend indipendente.
Locale e lifecycle
Imposta la locale iniziale in init, poi usa update se il visitatore cambia lingua:
CoperaOmni('init', {
channelKey: 'YOUR_CHANNEL_KEY',
locale: 'en',
});
function onApplicationLocaleChanged(locale) {
CoperaOmni('update', { locale });
}
Per una single-page application:
- Chiama
inituna sola volta per l'integrazione widget montata. - Iscriviti con
one rimuovi i listener conoffquando il componente proprietario unmounta. - Chiama
logoutquando l'utente dell'applicazione fa sign-out o cambia. - Chiama
destroyquando l'integrazione widget stessa viene rimossa permanentemente o quando ti serve intenzionalmente un'istanza fresca.
Risoluzione dei problemi
Lo script del widget non si carica
Proteggi il caricamento dello script. Lo snippet CDN diretto può rilevare un fallimento di network o CSP con l'evento error dell'elemento script:
<script>
(function (w, d) {
w.CoperaOmni = w.CoperaOmni || function () {
(w.CoperaOmni.q = w.CoperaOmni.q || []).push(arguments);
};
var s = d.createElement('script');
s.async = 1;
s.src = 'https://widget.copera.ai/widget.js';
s.onerror = function () {
console.error('The Copera Omni widget could not be loaded.');
};
d.head.appendChild(s);
})(window, document);
CoperaOmni('init', { channelKey: 'YOUR_CHANNEL_KEY' });
</script>
Se la request è bloccata, controlla la Content Security Policy e conferma che l'origin della host page sia in Allowed origins.
Come confermo che l'inizializzazione è riuscita?
init, identify, logout e destroy possono restituire promise dopo che il runtime live è installato. Attendi le transition di identità e sessione e gestisci il rejection. Quando chiami il dispatcher live direttamente o usi il wrapper tipizzato, attendi init() per confermare l'inizializzazione backend.
I comandi messi in coda prima che il runtime si installi sono fire-and-forget; l'evento ready conferma solo che l'iframe è pronto e che il suo stato iniziale è stato dispatchato — non prova che l'inizializzazione backend sia riuscita.
Le request di identity-token falliscono
- Restituisci un errore generico dal backend; non inoltrare mai la server key o il body di response del provider al browser.
- Ritenta solo i fallimenti server transienti con backoff limitato.
- Emetti un nuovo token dopo la scadenza invece di riprodurre un vecchio token.
- Tratta un
401dall'endpoint token come server key mancante, malformata o revocata, e ruotala o sostituiscila. - Tratta un
403come source widget non disponibile, e verifica che la source sia connessa e non sospesa.
Content Security Policy
Allowlist delle origin Copera per una CSP strict
Se il sito usa Content Security Policy (CSP), consenti le origin Copera minime richieste nelle direttive rilevanti:
script-src:https://widget.copera.aiperwidget.js.frame-src:https://widget.copera.aiper l'iframe del chat panel.connect-src:https://api.copera.aiper le request fatte dalla host page.style-src: Il runtime widget corrente crea elementi style e attributi style e non supporta un'alternativa nonce o hash. L'integrazione CSP strict richiede quindi'unsafe-inline'instyle-srcsolo — non aggiungerlo mai ascript-src.- La policy deve anche consentire il bootstrap inline mostrato in Quick embed. Se la policy blocca gli script inline, sposta il bootstrap in un file esterno consentito o autorizzalo con la policy nonce o hash del sito. Non indebolire la policy con
'unsafe-inline'inscript-src. - Configura l'origin della host page di embedding sotto Allowed origins, ad esempio
https://support.example.com— nonhttps://widget.copera.ai. Il permesso CSP non sostituisce il check origin di Copera.
Dopo aver cambiato la CSP, testa sotto enforcement CSP: verifica il launcher, l'apertura del panel, l'avvio di una conversazione, la ricezione di un aggiornamento unread e l'identità verificata nella console browser senza request bloccate.
Wrapper tipizzato e runtime CDN
Il runtime CDN a https://widget.copera.ai/widget.js è il widget stesso ed è tutto ciò di cui un'integrazione HTML semplice ha bisogno. @copera/omni-widget-sdk è un wrapper tipizzato opzionale per applicazioni TypeScript: inietta lo stesso runtime CDN una volta, bufferizza le chiamate early ed espone una facciata tipizzata per gli stessi comandi ed eventi. Non sostituisce il widget CDN né cambia il flusso di identità server.
Usa gli esempi di runtime diretto in questa guida a meno che l'applicazione non consumi già il wrapper tipizzato. L'installazione del package e le istruzioni alternative package-CDN sono intenzionalmente fuori da questa guida di embed.
Domande frequenti
Il widget web Copera Omni è gratuito?
Il web widget è gratuito. I numeri WhatsApp collegati a un channel Omni sono add-on a pagamento, e rimuovere una source WhatsApp o eliminare il suo channel non annulla lo slot del numero acquistato. Riduci la quantità acquistata separatamente in Workspace Billing.
Il channelKey del widget è sicuro da usare nel codice browser?
Sì. Il channelKey è la chiave pubblica di una source web-widget e va nell'embed code. Non è un channel ID, un source ID o una server API key privata cp_omni_sk_…. Rigenerare il channelKey interrompe immediatamente gli embed che usano ancora il vecchio valore.
Come identifico in modo sicuro un visitatore autenticato?
Autentica il visitatore nel backend dell'applicazione, scambia la key cp_omni_sk_… legata alla source con un identity token di cinque minuti e restituisci al browser solo quel token a breve durata. Poi chiama CoperaOmni('identify', { identityToken }). Non esporre mai la server API key al browser o a un client mobile.
Come restringo quali siti possono incorporare il widget?
Scegli Browser origin (recommended) e aggiungi ogni origin esatta consentita, incluso schema e porta quando presenti, ad Allowed origins. Aggiungi i subdomain separatamente. I permessi Content Security Policy non sostituiscono il check allowed-origin di Copera.
Documentazione correlata
Come la credenziale backend legata alla source differisce dai token Public API generali.
Schemi esatti di request, response ed errore per l'endpoint identity-token.
Messaging Public API per i channel Copera regolari.
Come l'endpoint identity-token Omni si inserisce nella Copera Public API.