Skip to content

How to embed AnnA Chat with a Corporate User V3

This guide shows how to embed AnnA Chat for an authenticated Corporate User using the V3 API. The browser receives only the generated AnnA Chat URL. Keep the company hash, user credentials, bearer token, and all V3 API calls on your backend.

You need the following information from your AnnA environment:

  • The company hash.
  • The login and password used with REST/Credentials/GetToken.
  • The Corporate User ID and, optionally, the user name.
  • The base URL of your AnnA environment.

V3 uses token authentication instead of the encryption keys required by V2. The token is short-lived and must never be exposed in the client-side page.

  1. Your backend calls REST/Credentials/GetToken with the company hash and service credentials.
  2. Your backend uses the returned token in an Authorization: Bearer <Token> header.
  3. Your backend calls REST/AnnACorporate/User/UserHash or REST/AnnACorporate/User/UserHashForced with getAnnAChatLink set to true.
  4. Your backend reads accessData.AnnAChatURL from the response.
  5. Your client-side code uses that generated URL as the iframe source.

Use UserHash when the Corporate User already exists. Use UserHashForced when AnnA should create or update the user with the supplied userName before generating the hash.

The generated URL can expire. Generate it on the server for the signed-in user and do not put the company hash, login, password, or bearer token in browser code.

First request a token:

Endpoint: REST/Credentials/GetToken

{
"hash": "YOUR_COMPANY_HASH",
"user": "YOUR_API_USER",
"pass": "YOUR_API_PASSWORD"
}

Then call the Corporate User endpoint with the token:

Endpoint: REST/AnnACorporate/User/UserHash

{
"getAnnAChatLink": true,
"hash": "YOUR_COMPANY_HASH",
"userId": "USER_ID",
"startService": ""
}

For a user that may need to be created or updated, use REST/AnnACorporate/User/UserHashForced and add the userName property:

{
"getAnnAChatLink": true,
"hash": "YOUR_COMPANY_HASH",
"userId": "USER_ID",
"userName": "USER_NAME",
"startService": ""
}

The successful response contains the URL in 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"
}
}

If success is false, handle the message value on the backend and do not send an invalid URL to the browser.

Replace ANNACHAT_URL_FROM_BACKEND with the accessData.AnnAChatURL value returned by your backend. The launcher creates the chat window only after the user clicks it.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AnnA Chat Embedded Demo</title>
</head>
<body>
<h1>Corporate client site</h1>
<p>The authenticated AnnA Chat widget appears in the lower-right corner.</p>
<script>
(function () {
const ANNA_URL = "ANNACHAT_URL_FROM_BACKEND"; // Use the backend-generated AnnAChatURL value
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>

The examples below call REST/Credentials/GetToken, then call REST/AnnACorporate/User/UserHash with getAnnAChatLink set to true. They parse accessData.AnnAChatURL and return it from a backend method.

To use the forced flow, replace REST/AnnACorporate/User/UserHash with REST/AnnACorporate/User/UserHashForced and add userName to the request body.

using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
public async Task<string> GenerateCorporateChatUrlAsync(
string userId, string userName = null)
{
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"
});
tokenResult.EnsureSuccessStatusCode();
var tokenResponse = JObject.Parse(await tokenResult.Content.ReadAsStringAsync());
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", (string)tokenResponse["data"]["Token"]);
var endpoint = string.IsNullOrEmpty(userName)
? "REST/AnnACorporate/User/UserHash"
: "REST/AnnACorporate/User/UserHashForced";
var request = new JObject
{
["getAnnAChatLink"] = true,
["hash"] = "YOUR_COMPANY_HASH",
["userId"] = userId,
["startService"] = ""
};
if (!string.IsNullOrEmpty(userName))
{
request["userName"] = userName;
}
var chatResult = await http.PostAsJsonAsync(endpoint, request);
chatResult.EnsureSuccessStatusCode();
var response = JObject.Parse(await chatResult.Content.ReadAsStringAsync());
if ((bool?)response["success"] != true)
{
throw new InvalidOperationException((string)response["message"]);
}
return (string)response["accessData"]["AnnAChatURL"];
}
}

Use the value returned in accessData.AnnAChatURL as ANNACHAT_URL_FROM_BACKEND in the client-side example. Do not return the company hash, API credentials, or bearer token to the browser.