Cómo integrar AnnA Chat con un usuario corporativo V3
Esta guía muestra cómo integrar AnnA Chat para un usuario corporativo autenticado mediante la API V3. El navegador recibe únicamente la URL generada de AnnA Chat. Mantén el hash de la empresa, las credenciales del usuario, el token bearer y todas las llamadas a la API V3 en tu backend.
Antes de comenzar
Sección titulada «Antes de comenzar»Necesitas la siguiente información de tu entorno AnnA:
- El hash de la empresa.
- El usuario y la contraseña utilizados con
REST/Credentials/GetToken. - El ID del usuario corporativo y, opcionalmente, su nombre.
- La URL base de tu entorno AnnA.
V3 usa autenticación mediante token en lugar de las claves de cifrado requeridas por V2. El token tiene una duración corta y nunca debe exponerse en la página del cliente.
Flujo de integración
Sección titulada «Flujo de integración»- Tu backend llama a
REST/Credentials/GetTokencon el hash de la empresa y las credenciales del servicio. - Tu backend usa el token devuelto en el encabezado
Authorization: Bearer <Token>. - Tu backend llama a
REST/AnnACorporate/User/UserHashoREST/AnnACorporate/User/UserHashForcedcongetAnnAChatLinkestablecido entrue. - Tu backend lee
accessData.AnnAChatURLde la respuesta. - Tu código del cliente usa esa URL generada como origen del iframe.
Usa UserHash cuando el usuario corporativo ya existe. Usa UserHashForced cuando AnnA deba crear o actualizar el usuario con el userName proporcionado antes de generar el hash.
La URL generada puede caducar. Genérala en el servidor para el usuario autenticado y no coloques el hash de la empresa, el usuario, la contraseña ni el token bearer en el código del navegador.
Obtener una URL del chat
Sección titulada «Obtener una URL del chat»Primero solicita un token:
Endpoint: REST/Credentials/GetToken
{ "hash": "YOUR_COMPANY_HASH", "user": "YOUR_API_USER", "pass": "YOUR_API_PASSWORD"}Después llama al endpoint del usuario corporativo con el token:
Endpoint: REST/AnnACorporate/User/UserHash
{ "getAnnAChatLink": true, "hash": "YOUR_COMPANY_HASH", "userId": "USER_ID", "startService": ""}Para un usuario que deba crearse o actualizarse, usa REST/AnnACorporate/User/UserHashForced y agrega la propiedad userName.
Respuesta de AnnA
Sección titulada «Respuesta de AnnA»La respuesta correcta contiene la URL en accessData.AnnAChatURL:
{ "success": true, "message": "User Hash generated successfully", "accessData": { "Hash": "USER_HASH", "ChatURL": "CHAT_URL_WITH_USER_HASH", "EmbeddedURL": "EMBEDDED_URL_WITH_USER_HASH", "AnnAChatURL": "ANNACHAT_URL_FOR_IFRAME", "Expires": "EXPIRATION_DATETIME" }}Si success es false, gestiona el valor de message en el backend y no envíes una URL inválida al navegador.
Ejemplo completo
Sección titulada «Ejemplo completo»Reemplaza ANNACHAT_URL_FROM_BACKEND por el valor de accessData.AnnAChatURL devuelto por tu backend. El botón crea la ventana del chat solamente después de que el usuario hace clic.
<!doctype html><html lang="es"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Demo de AnnA Chat integrado</title> </head> <body> <h1>Sitio del cliente corporativo</h1> <p> El widget autenticado de AnnA Chat aparece en la esquina inferior derecha. </p>
<script> (function () { const ANNA_URL = "ANNACHAT_URL_FROM_BACKEND"; // Usa el valor AnnAChatURL generado por el backend const CHAT_W = 400, CHAT_H = 600, BTN_SIZE = 60; const sideOffset = "clamp(16px, 2.2vw, 30px)"; const bottomOffset = "clamp(16px, 2.2vw, 30px)";
const btn = document.createElement("div"); btn.id = "anna-launcher"; Object.assign(btn.style, { position: "fixed", bottom: bottomOffset, right: sideOffset, width: BTN_SIZE + "px", height: BTN_SIZE + "px", borderRadius: "50%", cursor: "pointer", zIndex: "9999", background: "#7c3aed", display: "flex", alignItems: "center", justifyContent: "center", boxShadow: "0 4px 12px rgba(0,0,0,0.25)", transition: "transform 0.2s", }); btn.innerHTML = '<svg width="28" height="28" fill="#fff" viewBox="0 0 24 24"><path d="M20 2H4a2 2 0 0 0-2 2v18l4-4h14a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z"/></svg>'; btn.onmouseenter = function () { btn.style.transform = "scale(1.1)"; }; btn.onmouseleave = function () { btn.style.transform = "scale(1)"; };
const chat = document.createElement("div"); chat.id = "anna-chat"; Object.assign(chat.style, { position: "fixed", bottom: `calc(${bottomOffset} + ${BTN_SIZE}px + 10px)`, right: sideOffset, width: `min(${CHAT_W}px, calc(100vw - 32px))`, height: CHAT_H + "px", maxHeight: `min(${CHAT_H}px, calc(100vh - 116px))`, borderRadius: "12px", overflow: "hidden", boxShadow: "0 8px 30px rgba(0,0,0,0.3)", display: "none", zIndex: "9998", }); chat.innerHTML = '<iframe src="' + ANNA_URL + '" style="width:100%;height:100%;border:none" allow="camera *; microphone *; geolocation *"></iframe>';
btn.addEventListener("click", function () { var open = chat.style.display === "none"; chat.style.display = open ? "block" : "none"; });
document.body.appendChild(chat); document.body.appendChild(btn); })(); </script> </body></html>Generar la URL del chat en el backend
Sección titulada «Generar la URL del chat en el backend»Los ejemplos siguientes llaman a REST/Credentials/GetToken y después a REST/AnnACorporate/User/UserHash con getAnnAChatLink establecido en true. Analizan accessData.AnnAChatURL y devuelven el valor desde un método del backend.
Para usar el flujo forzado, reemplaza REST/AnnACorporate/User/UserHash por REST/AnnACorporate/User/UserHashForced y agrega userName al cuerpo de la solicitud.
using var http = new HttpClient { BaseAddress = new Uri("https://YOUR_ANNA_ENVIRONMENT/") };var tokenResult = await http.PostAsJsonAsync("REST/Credentials/GetToken", new{ hash = "YOUR_COMPANY_HASH", user = "YOUR_API_USER", pass = "YOUR_API_PASSWORD"});var token = JObject.Parse(await tokenResult.Content.ReadAsStringAsync())["data"]["Token"];http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", (string)token);
var chatResult = await http.PostAsJsonAsync("REST/AnnACorporate/User/UserHash", new{ getAnnAChatLink = true, hash = "YOUR_COMPANY_HASH", userId = "USER_ID", startService = ""});var response = JObject.Parse(await chatResult.Content.ReadAsStringAsync());if ((bool?)response["success"] != true) throw new InvalidOperationException((string)response["message"]);var annaChatUrl = (string)response["accessData"]["AnnAChatURL"];using var http = new HttpClient { BaseAddress = new Uri("https://YOUR_ANNA_ENVIRONMENT/") };var tokenResult = await http.PostAsJsonAsync("REST/Credentials/GetToken", new{ hash = "YOUR_COMPANY_HASH", user = "YOUR_API_USER", pass = "YOUR_API_PASSWORD"});var token = (await tokenResult.Content.ReadFromJsonAsync<TokenResponse>())!.Data.Token;http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var result = await http.PostAsJsonAsync("REST/AnnACorporate/User/UserHash", new{ getAnnAChatLink = true, hash = "YOUR_COMPANY_HASH", userId = "USER_ID", startService = ""});var response = await result.Content.ReadFromJsonAsync<ChatResponse>();if (response?.Success != true) throw new InvalidOperationException(response?.Message);var annaChatUrl = response.AccessData.AnnAChatURL;var client = HttpClient.newHttpClient();var baseUrl = "https://YOUR_ANNA_ENVIRONMENT/";var tokenRequest = HttpRequest.newBuilder().uri(URI.create(baseUrl + "REST/Credentials/GetToken")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"hash\":\"YOUR_COMPANY_HASH\",\"user\":\"YOUR_API_USER\",\"pass\":\"YOUR_API_PASSWORD\"}")) .build();var tokenResult = client.send(tokenRequest, HttpResponse.BodyHandlers.ofString());var token = new ObjectMapper().readTree(tokenResult.body()).get("data").get("Token").asText();
var body = "{\"getAnnAChatLink\":true,\"hash\":\"YOUR_COMPANY_HASH\",\"userId\":\"USER_ID\",\"startService\":\"\"}";var chatRequest = HttpRequest.newBuilder().uri(URI.create(baseUrl + "REST/AnnACorporate/User/UserHash")) .header("Content-Type", "application/json").header("Authorization", "Bearer " + token) .POST(HttpRequest.BodyPublishers.ofString(body)).build();var response = new ObjectMapper().readTree(client.send(chatRequest, HttpResponse.BodyHandlers.ofString()).body());var annaChatUrl = response.get("accessData").get("AnnAChatURL").asText();<?phpfunction postJson(string $url, array $payload, ?string $token = null): array { $headers = ["Content-Type: application/json"]; if ($token !== null) $headers[] = "Authorization: Bearer " . $token; $curl = curl_init($url); curl_setopt_array($curl, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => json_encode($payload)]); $body = curl_exec($curl); curl_close($curl); return json_decode($body, true, 512, JSON_THROW_ON_ERROR);}$baseUrl = "https://YOUR_ANNA_ENVIRONMENT/";$tokenResponse = postJson($baseUrl . "REST/Credentials/GetToken", [ "hash" => "YOUR_COMPANY_HASH", "user" => "YOUR_API_USER", "pass" => "YOUR_API_PASSWORD"]);$response = postJson($baseUrl . "REST/AnnACorporate/User/UserHash", [ "getAnnAChatLink" => true, "hash" => "YOUR_COMPANY_HASH", "userId" => "USER_ID", "startService" => ""], $tokenResponse["data"]["Token"]);$annaChatUrl = $response["accessData"]["AnnAChatURL"];&tokenBody = !'{"hash":"YOUR_COMPANY_HASH","user":"YOUR_API_USER","pass":"YOUR_API_PASSWORD"}'&tokenResponse = PostJson(!"YOUR_ANNA_ENVIRONMENT/REST/Credentials/GetToken", &tokenBody, !"")&tokenResponse.FromJson(&tokenData)&chatBody = !'{"getAnnAChatLink":true,"hash":"YOUR_COMPANY_HASH","userId":"USER_ID","startService":""}'&chatResponse = PostJson(!"YOUR_ANNA_ENVIRONMENT/REST/AnnACorporate/User/UserHash", &chatBody, !"Bearer " + &tokenData.Data.Token)&chatResponse.FromJson(&chatData)&annaChatUrl = &chatData.AccessData.AnnAChatURLimport requests
base_url = "https://YOUR_ANNA_ENVIRONMENT/"token_response = requests.post(f"{base_url}REST/Credentials/GetToken", json={ "hash": "YOUR_COMPANY_HASH", "user": "YOUR_API_USER", "pass": "YOUR_API_PASSWORD"}, timeout=30)token_response.raise_for_status()token = token_response.json()["data"]["Token"]result = requests.post(f"{base_url}REST/AnnACorporate/User/UserHash", headers={"Authorization": f"Bearer {token}"}, json={ "getAnnAChatLink": True, "hash": "YOUR_COMPANY_HASH", "userId": "USER_ID", "startService": ""}, timeout=30)result.raise_for_status()response = result.json()if response.get("success") is not True: raise RuntimeError(response.get("message", "AnnA request failed"))anna_chat_url = response["accessData"]["AnnAChatURL"]Usa el valor devuelto en accessData.AnnAChatURL como ANNACHAT_URL_FROM_BACKEND en el ejemplo del cliente. No devuelvas el hash de la empresa, las credenciales de la API ni el token bearer al navegador.
Ver también
Sección titulada «Ver también»- Cómo acceder — cómo se comporta el enlace corporativo para quien lo usa.
- Requisitos y límites — requisitos del navegador y permisos.