Widget de chat web de Copera Omni: configuración, SDK e identidad
El widget web de Copera Omni agrega un chat web a tu sitio y envía cada conversación a un canal Omni: una bandeja de entrada compartida que puede recibir conversaciones de varias fuentes a la vez, como uno o más widgets web y números de WhatsApp, para que tu equipo trabaje con todas ellas en un solo lugar.
Esta guía te lleva desde una instalación de dos minutos hasta una integración lista para producción: la inserción de copiar y pegar, la API de comandos CoperaOmni para iniciadores personalizados, la seguridad por origen del navegador y el flujo seguro para identificar a visitantes autenticados sin exponer nunca una clave de servidor en el navegador.
Fuentes gratuitas frente a de pago
Un canal Omni puede combinar varias fuentes. El widget web es el gratuito; WhatsApp es un complemento de pago.
Agrega tantas fuentes de widget web como necesites, sin costo. Todo en esta guía usa el widget web gratuito.
Conecta números de WhatsApp al mismo canal Omni como complementos de pago, para que las conversaciones de la web y de WhatsApp lleguen a una sola bandeja de entrada.
Eliminar una fuente de WhatsApp o eliminar un canal Omni no cancela su cupo de número adquirido. Para dejar de pagar por un cupo, reduce por separado la cantidad contratada en Facturación del workspace.
Inicio rápido
Dos minutos hasta tener un widget en producción.
En Copera, abre (o crea) un canal Omni, ve a Configuración → Agregar fuente y elige Widget web — Copera identifica esta fuente como Gratis. Luego copia el channelKey que aparece en Configurar e instalar.
Elige Quick embed o JavaScript SDK abajo, pégalo en tu página y sustituye YOUR_CHANNEL_KEY por la clave que copiaste.
El iniciador flotante de Copera aparece en la posición que configuraste. Ábrelo para comenzar una conversación.
- Quick embed
- JavaScript SDK
Coloca este fragmento completo una sola vez, justo antes de la etiqueta de cierre </body>. Carga de forma asíncrona el runtime de la CDN, inicializa la fuente y muestra el iniciador flotante de 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>
Eso es todo lo que necesita un sitio HTML sencillo. Para controlar el panel desde tu propio botón en lugar del iniciador flotante, cambia a la pestaña JavaScript SDK.
Carga el mismo runtime de la CDN, inicialízalo con launcher: 'custom' para ocultar el iniciador flotante de Copera y controla el panel desde tu propio control con la API de comandos CoperaOmni.
<button id="support-chat" type="button" aria-controls="copera-support" aria-expanded="false">
Hablar con soporte
</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 y close cuando tu aplicación ya conozca el estado deseado, y toggle para un único botón iniciador. Si mantuviste el iniciador predeterminado, setLauncherVisible(false) lo oculta temporalmente y setLauncherVisible(true) lo restaura.
@copera/omni-widget-sdk es un wrapper tipado opcional en torno a estos mismos comandos. Consulta Runtime de CDN y wrapper tipado.
El channelKey generado es la clave pública de esta fuente específica del widget web. Es seguro incluirlo en código del navegador. No es el ID del canal Omni ni el ID de la fuente, y no debe sustituirse por ninguno de ellos.
Si seleccionas Regenerar clave, el channelKey anterior deja de funcionar. Sustitúyelo en todos los lugares donde esté insertado el widget.
Configurar el widget
El inicio rápido usa los valores predeterminados. Abre la Configuración de la fuente del widget (o Configurar e instalar) para adaptar toda la experiencia antes de pasar a producción.
Crea un canal del tipo Omni o abre uno existente. Un mismo canal Omni puede recibir conversaciones de varias fuentes.
Abre la Configuración del canal, selecciona Agregar fuente y luego Widget web. Copera identifica esta fuente como Gratis.
Introduce un Nombre visible, elige el Color principal y la Posición del iniciador y, si quieres, define un Mensaje de bienvenida. También puedes activar Mostrar un formulario previo al chat para visitantes anónimos.
Para una integración nueva, elige Origen del navegador (recomendado) en Seguridad de inserción. Agrega a Orígenes permitidos cada origen exacto del sitio que pueda insertar el widget y presiona Enter después de cada uno — por ejemplo, https://www.example.com y https://app.example.com.
Deja desactivado Requerir identidad de visitante verificada para permitir conversaciones anónimas o con formulario previo. Actívalo cuando tu sitio siempre llame a identify con un token de identidad emitido por el servidor. El formulario previo y la identidad verificada obligatoria no pueden activarse a la vez.
Selecciona Crear widget. En la pantalla de confirmación, copia Quick embed para el iniciador integrado o selecciona JavaScript SDK para uno personalizado. Más adelante puedes volver a la fuente y seleccionar Configurar e instalar.
| Ajuste | Qué controla | Valor predeterminado |
|---|---|---|
| Nombre visible | El nombre que aparece en la parte superior del panel de chat. | Nombre de la fuente |
| Color principal | El color de acento del iniciador y del panel. | Color de acento del workspace |
| Posición del iniciador | En qué esquina se ubica el iniciador flotante. | Inferior derecha |
| Mensaje de bienvenida | Un primer mensaje opcional que se muestra al abrir el panel. | Ninguno |
| Mostrar un formulario previo al chat | Pide datos a los visitantes anónimos antes de comenzar la conversación. | Desactivado |
| Seguridad de inserción | Cómo decide Copera qué páginas pueden insertar el widget. | Origen del navegador |
| Requerir identidad de visitante verificada | Bloquea los chats anónimos y con formulario previo; exige un token de identidad emitido por el servidor. | Desactivado |
API de comandos de JavaScript
https://widget.copera.ai/widget.js instala la función global de comandos CoperaOmni(command, ...args). Las llamadas realizadas mediante el fragmento de cola antes de que termine de cargar el runtime se almacenan y se reproducen en orden.
Comandos
| Comando | Firma | Propósito |
|---|---|---|
init | CoperaOmni('init', config) | Inicializa el widget. config.channelKey es obligatorio; locale y launcher son opcionales. |
identify | CoperaOmni('identify', identity) | Define la identidad del visitante. Prefiere { identityToken } para usuarios autenticados; { name, email } admite datos no verificados del visitante. |
logout | CoperaOmni('logout') | Borra la identidad actual antes de que un usuario cierre sesión o de que otro usuario tome el control de la página. |
destroy | CoperaOmni('destroy') | Elimina el iframe, el iniciador, los temporizadores, los listeners y las credenciales en memoria. |
open | CoperaOmni('open') | Abre el panel de chat. |
close | CoperaOmni('close') | Cierra el panel de chat. |
toggle | CoperaOmni('toggle') | Alterna el panel entre abierto y cerrado. |
setLauncherVisible | CoperaOmni('setLauncherVisible', visible) | Muestra u oculta el iniciador integrado sin destruir el widget. |
update | CoperaOmni('update', { locale }) | Cambia el idioma del widget activo. |
on | CoperaOmni('on', eventName, callback) | Se suscribe a un evento. El runtime activo devuelve una función para cancelar la suscripción. |
off | CoperaOmni('off', eventName, callback) | Elimina el mismo callback que se pasó antes a on. |
Las opciones compatibles de init son:
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
channelKey | string | Obligatorio | Clave pública de la fuente copiada de Configurar e instalar. |
locale | string | "en" | Locale BCP 47 de la interfaz del widget, como "en" o "pt-BR". |
launcher | "default" | "custom" | "default" | Usa "custom" para ocultar el iniciador de Copera y controlar el panel por tu cuenta. |
Eventos
Cada callback de evento recibe un objeto cuyo name identifica el evento.
| Evento | Payload | Cuándo se activa |
|---|---|---|
ready | { name: 'ready' } | El iframe está listo y ha enviado su estado inicial. Esto no demuestra que la inicialización del backend haya terminado correctamente. |
open | { name: 'open' } | Se abre el panel. |
close | { name: 'close' } | Se cierra el panel. |
identityRequired | { name: 'identityRequired' } | Se está reiniciando una conversación cerrada en una fuente que exige volver a verificar al visitante. |
unreadCount | { name: 'unreadCount', count: number } | Cambia el número de mensajes sin leer. |
conversationStarted | { name: 'conversationStarted', conversationId: string } | Comienza una conversación y su ID queda disponible. |
Actualiza un indicador personalizado y registra el inicio de una conversación:
const unreadBadge = document.querySelector('#support-unread');
function handleUnread(event) {
unreadBadge.textContent = String(event.count);
unreadBadge.hidden = event.count === 0;
}
function handleConversationStarted(event) {
console.log('Conversación de Omni iniciada', event.conversationId);
}
CoperaOmni('on', 'unreadCount', handleUnread);
CoperaOmni('on', 'conversationStarted', handleConversationStarted);
// Cuando se elimine este componente de la página:
CoperaOmni('off', 'unreadCount', handleUnread);
CoperaOmni('off', 'conversationStarted', handleConversationStarted);
Conserva la referencia del callback si vas a llamar a off; una nueva función inline no es el mismo listener.
Usa eventos del ciclo de vida para mantener sincronizada la interfaz 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 () {
// Pide a la aplicación host autenticada que renueve su token de identidad.
void identifyCurrentUser();
});
Identidad del visitante
Elige cuánto sabes de cada visitante. Los visitantes anónimos y con formulario previo no necesitan backend; los visitantes verificados usan un token de corta duración emitido por el servidor.
- Anónimo
- Formulario previo
- Verificado
Deja desactivado Requerir identidad de visitante verificada. Un visitante puede comenzar sin datos de identidad. Copera mantiene asociada a la conversación la sesión del widget del navegador.
Activa Mostrar un formulario previo al chat para pedir datos al visitante antes de comenzar la conversación. La información que introduce el visitante aporta contexto útil, pero no demuestra que sea titular de esa identidad.
Para un cliente autenticado, tu backend del mismo origen intercambia su clave de API de servidor vinculada a la fuente por un token de identidad de corta duración. El navegador pasa únicamente ese token a identify. Activa Requerir identidad de visitante verificada cuando debas bloquear las conversaciones anónimas y con formulario previo.
Llamar a identify con { name, email } proporciona datos no verificados del visitante. Para la identidad de una cuenta autenticada, usa { identityToken } — se describe a continuación.
Identidad verificada segura
La identidad verificada tiene tres límites:
- Copera emite una clave de API de servidor vinculada a la fuente con el prefijo
cp_omni_sk_. - Tu backend envía a Copera un identificador estable del cliente y recibe un token de identidad válido durante cinco minutos.
- El navegador recibe únicamente el token de corta duración y lo pasa al widget.
La clave cp_omni_sk_… solo debe estar en el almacén de secretos de tu backend. Nunca la incluyas en HTML, JavaScript del navegador, una aplicación móvil, logs, propiedades de analítica ni un repositorio público. El channelKey público y la clave de API de servidor privada tienen fines distintos y no son intercambiables.
Abre el panel Configurar e instalar de la fuente del widget. En Claves de API del servidor, selecciona Crear clave de servidor, asígnale un nombre para un backend o entorno y cópiala de inmediato — solo se muestra una vez. Se admiten varias claves activas, lo que permite rotarlas sin compartir una credencial entre entornos.
Guárdala como una variable de entorno exclusiva del backend, por ejemplo COPERA_OMNI_SERVER_KEY.
Tu backend llama al endpoint de tokens de identidad con la clave de servidor:
POST https://api.copera.ai/public/v1/omni-channel/identity-tokens
Authorization: Bearer cp_omni_sk_...
Content-Type: application/json
El cuerpo de la solicitud admite:
| Campo | Obligatorio | Descripción |
|---|---|---|
externalId | Sí | Un identificador opaco, estable y sin PII para el cliente autenticado, limitado a tu aplicación. Usa el mismo valor en futuras visitas. |
name | No | Nombre visible actual, obtenido de tu registro de cuenta confiable. |
email | No | Dirección de correo actual, obtenida de tu registro de cuenta confiable. |
La clave de servidor ya vincula la solicitud con un workspace y una fuente de widget web; no envíes un ID de workspace, ID de fuente, ID de canal ni un channelKey en esta solicitud. Consulta la referencia de la API para crear un token de identidad de Omni para ver el esquema exacto y las respuestas.
La siguiente ruta de Node.js ilustra el patrón del mismo origen. Supone que tu aplicación ya autenticó la solicitud y expuso al usuario autenticado como request.user.
export async function postOmniIdentity(request, response) {
const user = request.user;
if (!user) {
return response.status(401).json({ message: 'Debes iniciar sesión' });
}
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: 'No se pudo iniciar el chat de soporte' });
}
const { identityToken, expiresAt } = await coperaResponse.json();
response.setHeader('Cache-Control', 'no-store');
return response.json({ identityToken, expiresAt });
}
Para un mismo externalId estable dentro de esa fuente de widget, Copera crea el contacto la primera vez y reutiliza ese mismo contacto en futuras sesiones verificadas. Usa un valor opaco creado para tu aplicación. No uses un nombre visible, correo electrónico, teléfono, nombre de usuario, ID secuencial de base de datos ni otro valor directamente identificable o que pueda cambiar.
Expón la ruta en el mismo origen que tu aplicación, exige la sesión autenticada habitual y las protecciones CSRF de la aplicación, y devuelve únicamente el token de corta duración:
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(`Falló la solicitud de identidad de Omni: ${response.status}`);
}
const { identityToken } = await response.json();
await CoperaOmni('identify', { identityToken });
}
CoperaOmni('init', { channelKey: 'YOUR_CHANNEL_KEY' });
void identifyCurrentUser().catch(function (error) {
console.error('No se pudo identificar al visitante de Omni', error);
});
Los tokens de identidad caducan cinco minutos después de emitirse. Genéralos bajo demanda y úsalos de inmediato; no los guardes en almacenamiento local ni los trates como un token de inicio de sesión duradero.
Llama a CoperaOmni('logout') y espera su resultado antes de identificar a otro usuario autenticado en la misma página del navegador. Al cerrar sesión en la aplicación, llama a logout aunque tengas previsto mantener disponible el chat anónimo. Así evitas que la identidad de un cliente pase a la sesión de otro cliente.
async function switchOmniUser(nextUserIsSignedIn) {
await CoperaOmni('logout');
if (nextUserIsSignedIn) {
await identifyCurrentUser();
}
}
Cuando un visitante reinicia una conversación cerrada y Requerir identidad de visitante verificada está activado, el widget emite identityRequired para que el sitio vuelva a verificarlo. Solicita un token nuevo y llama a identify; no recurras al name y email proporcionados por el navegador como si estuvieran verificados. En la primera visita autenticada, identifica al visitante de forma proactiva después de init, como se muestra arriba.
Seguridad por origen del navegador
Origen del navegador (recomendado) compara la página host que inserta el widget con los Orígenes permitidos de la fuente y establece una sesión de inserción de corta duración vinculada al origen. Agrega el origen de la página que aloja el widget, como https://support.example.com — no https://widget.copera.ai. Configura orígenes exactos, incluidos el esquema y el puerto cuando corresponda:
| URL de la página | Origen permitido |
|---|---|
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 |
Las rutas, consultas, fragmentos, nombres de usuario y contraseñas no forman parte de un origen. Agrega cada subdominio por separado. Fuera del desarrollo local, prefiere HTTPS.
Elige Origen del navegador (recomendado) para despliegues nuevos y configura explícitamente cada origen permitido. Cuando agregues o elimines un dominio de producción, actualiza Orígenes permitidos antes de desplegar allí la inserción. Eliminar un origen impide nuevas sesiones de inserción desde él.
Rotación y revocación
Trata la clave pública de inserción y las claves de API de servidor como credenciales independientes:
| Acción | Efecto inmediato | Paso posterior |
|---|---|---|
| Regenerar clave | Los fragmentos existentes que usan la channelKey anterior dejan de funcionar. | Sustituye la clave en cada inserción. |
| Revocar una clave de API de servidor | Las solicitudes de emisión de token que usan esa clave cp_omni_sk_… fallan de inmediato. | Despliega primero una clave de reemplazo si la integración debe seguir disponible. |
Para rotar la clave de servidor sin tiempo de inactividad, crea una segunda clave, despliégala en el backend, confirma que se actualice Último uso y luego revoca la anterior. Usa claves distintas para producción, staging y cada backend independiente.
Locale y ciclo de vida
Define el locale inicial en init y usa update si el visitante cambia de idioma:
CoperaOmni('init', {
channelKey: 'YOUR_CHANNEL_KEY',
locale: 'en',
});
function onApplicationLocaleChanged(locale) {
CoperaOmni('update', { locale });
}
Para una aplicación de página única:
- Llama a
inituna vez para la integración montada del widget. - Suscríbete con
ony elimina listeners conoffcuando se desmonte el componente propietario. - Llama a
logoutcuando el usuario de la aplicación cierre sesión o cambie. - Llama a
destroycuando se elimine permanentemente la integración del widget o cuando necesites intencionalmente una instancia nueva.
Solución de problemas
El script del widget no carga
Protege la carga del script. El fragmento directo de la CDN puede detectar un fallo de red o de CSP mediante el evento error del elemento de 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('No se pudo cargar el widget de Copera Omni.');
};
d.head.appendChild(s);
})(window, document);
CoperaOmni('init', { channelKey: 'YOUR_CHANNEL_KEY' });
</script>
Si se bloquea la solicitud, revisa tu Content Security Policy y confirma que el origen de la página host esté en Orígenes permitidos.
¿Cómo confirmo que la inicialización tuvo éxito?
init, identify, logout y destroy pueden devolver promesas después de instalarse el runtime activo. Espera las transiciones de identidad y sesión y gestiona los rechazos. Al llamar directamente al dispatcher activo o usar el wrapper tipado, espera init() para confirmar la inicialización del backend.
Los comandos en cola antes de instalarse el runtime son de tipo fire-and-forget; el evento ready solo confirma que el iframe está listo y que se envió su estado inicial — no demuestra que la inicialización del backend haya tenido éxito.
Las solicitudes de token de identidad fallan
- Devuelve un error genérico desde tu backend; nunca reenvíes al navegador la clave de servidor ni el cuerpo de la respuesta del proveedor.
- Reintenta solo los fallos transitorios del servidor con backoff limitado.
- Genera un token nuevo después de que caduque en vez de reutilizar uno anterior.
- Interpreta un
401del endpoint de tokens como una clave de servidor ausente, malformada o revocada, y rótala o sustitúyela. - Interpreta un
403como una fuente de widget no disponible y comprueba que esté conectada y no suspendida.
Content Security Policy
Permite los orígenes de Copera para una CSP estricta
Si tu sitio usa Content Security Policy (CSP), permite los orígenes mínimos necesarios de Copera en las directivas pertinentes:
script-src:https://widget.copera.aiparawidget.js.frame-src:https://widget.copera.aipara el iframe del panel de chat.connect-src:https://api.copera.aipara las solicitudes que realiza la página host.style-src: el runtime actual del widget crea elementos y atributos de estilo y no admite una alternativa con nonce o hash. Por lo tanto, la integración con una CSP estricta requiere'unsafe-inline'únicamente enstyle-src— nunca lo agregues ascript-src.- Tu política también debe permitir el bootstrap inline que se muestra en Quick embed. Si tu política bloquea los scripts inline, mueve el bootstrap a un archivo externo permitido o autorízalo con la política de nonce o hash de tu sitio. No debilites la política con
'unsafe-inline'enscript-src. - Configura en Orígenes permitidos el origen de la página host que inserta el widget, por ejemplo
https://support.example.com— nohttps://widget.copera.ai. El permiso de CSP no sustituye la comprobación de origen de Copera.
Después de cambiar la CSP, prueba con la política en modo de bloqueo: comprueba el iniciador, la apertura del panel, el inicio de una conversación, la recepción de una actualización de no leídos y la identidad verificada en la consola del navegador, sin ninguna solicitud bloqueada.
Runtime de CDN y wrapper tipado
El runtime de CDN en https://widget.copera.ai/widget.js es el propio widget y es todo lo que necesita una integración HTML sencilla. @copera/omni-widget-sdk es un wrapper tipado opcional para aplicaciones TypeScript: inyecta una vez el mismo runtime de CDN, almacena las llamadas tempranas y expone una fachada tipada para los mismos comandos y eventos. No sustituye el widget de CDN ni cambia el flujo de identidad del servidor.
Usa los ejemplos directos del runtime de esta guía salvo que tu aplicación ya consuma el wrapper tipado. La instalación del paquete y las instrucciones alternativas de CDN del paquete quedan intencionalmente fuera de esta guía de inserción.
Preguntas frecuentes
¿El widget web de Copera Omni es gratuito?
El widget web es gratuito. Los números de WhatsApp conectados a un canal Omni son complementos de pago, y eliminar una fuente de WhatsApp o su canal no cancela el cupo de número adquirido. Reduce por separado la cantidad contratada en Facturación del workspace.
¿Es seguro usar el channelKey del widget en código del navegador?
Sí. El channelKey es la clave pública de una fuente de widget web y debe estar en el código de inserción. No es un ID de canal, un ID de fuente ni la clave de API de servidor privada cp_omni_sk_…. Regenerar el channelKey detiene de inmediato las inserciones que aún usan el valor anterior.
¿Cómo identifico de forma segura a un visitante autenticado?
Autentica al visitante en el backend de tu aplicación, intercambia la clave cp_omni_sk_… vinculada a la fuente por un token de identidad de cinco minutos y devuelve al navegador únicamente ese token de corta duración. Luego llama a CoperaOmni('identify', { identityToken }). Nunca expongas la clave de API de servidor al navegador ni a un cliente móvil.
¿Cómo restrinjo qué sitios pueden insertar el widget?
Elige Origen del navegador (recomendado) y agrega a Orígenes permitidos cada origen exacto permitido, incluidos el esquema y el puerto cuando corresponda. Agrega los subdominios por separado. Los permisos de Content Security Policy no sustituyen la comprobación de orígenes permitidos de Copera.
Documentación relacionada
Cómo se diferencia la credencial de backend vinculada a la fuente de los tokens generales de la Public API.
Esquemas exactos de solicitud, respuesta y errores para el endpoint de token de identidad.
Mensajería de la Public API para channels normales de Copera.
Cómo encaja el endpoint de token de identidad de Omni en la Public API de Copera.