All posts

August 24, 2026 · 6 min read

Know who you are talking to: identify(), attributes, and why the hash matters

Anonymous visitors are fine on a landing page and useless inside a product. How to pass identity to the widget, what to put in attributes so the AI can use them, and how a signed hash stops anyone from reading someone else's chats.

Aditya SinghFounder, SvellyoDevelopersGuides

The difference between a support tool and a support tool you trust inside your product is whether it knows who is talking. "Visitor" with a random id is fine when someone is asking about pricing on the marketing site. It is not fine when the message is "my export has been broken since Tuesday" and you have to ask which account, which plan, and whether they are the admin.

This post covers the three layers of identity in Svellyo: telling the widget who the user is, giving it the attributes that make the AI and your team smarter, and signing the whole thing so nobody can pretend to be someone else.

Layer 1: identify

The simplest form is one call after login.

window.Svellyo("identify", {
  userId: "usr_8f3k2",
  email: "jane@northwind.io",
  name: "Jane Cooper",
});

With the React component, pass the same object as a user prop and it is called for you, and re-called when the user changes.

From that moment the inbox shows Jane by name, with her email in the contact panel, and every conversation she starts is linked to one contact record. If she opens the widget on her phone next week and is identified there too, she sees her earlier conversations and you see one person, not two visitors.

Two rules that save trouble later.

Use a stable id. userId should be the primary key of the user in your system, not the email. People change emails; you want the contact to survive that. Email is still worth sending, because it is how you reach them when they are offline.

Reset on logout. Call Svellyo("reset") in your logout handler, or set the user prop to null. It clears the visitor token so the next person on that browser starts fresh. Shared computers exist, and a chat history that follows the machine rather than the person is a privacy incident waiting to happen.

Layer 2: attributes

Attributes are the part most people skip and the part that pays off most.

window.Svellyo("identify", {
  userId: "usr_8f3k2",
  email: "jane@northwind.io",
  name: "Jane Cooper",
  attributes: {
    plan: "pro",
    seats: 4,
    company: "Northwind",
    signedUpAt: "2026-03-01",
    trialEndsAt: null,
    role: "admin",
  },
});

Flat keys, values that are strings, numbers, booleans or null. They show up in the contact panel next to every conversation, which alone saves the "which plan are you on?" exchange that opens half of all billing threads.

More importantly, attributes are part of the context the AI agent sees. When Jane asks "can I add a custom domain to my help center?", an agent that knows plan: "pro" answers "yes, it is included on your plan, here is how", while an agent that knows plan: "free" answers "that is available on Pro and Scale". Same knowledge base, correct answer for the person asking.

Good attributes are the ones that change what the right answer is. Plan, role, whether they are in a trial, which product features they have enabled, their locale. Bad attributes are ones you would not want in a support transcript: internal scores, anything sensitive, anything you would not say to the customer's face.

Update them when they change with setAttributes. Set a key to null to remove it.

Layer 3: verified identity

Here is the problem with everything above. It runs in the browser. Anyone can open the console and type:

window.Svellyo("identify", { userId: "usr_8f3k2", email: "jane@northwind.io" });

and, without protection, they would see Jane's conversations. For a marketing site with anonymous visitors that is irrelevant. For a product where support threads contain account details, it is unacceptable.

The fix is a signed hash. Your server, which knows who is actually logged in, computes an HMAC-SHA256 of the user id using a secret only it holds, and passes the result to the browser alongside the identity. The widget sends it up; Svellyo recomputes the hash with the same secret; if they do not match, the identify call is ignored and the visitor stays anonymous.

// server only
import { createHmac } from "node:crypto";

const hash = createHmac("sha256", process.env.SVELLYO_IDENTITY_SECRET!)
  .update(user.id)
  .digest("hex");
// browser
window.Svellyo("identify", { userId: user.id, email: user.email, name: user.name, hash });

The secret is on the Installation page in your dashboard. Keep it in your server environment, never in client code. Then switch on Require verified identity for the workspace. After that, an unsigned identify call does nothing, and a hash for one user id is useless for any other.

Other languages, for completeness: Python is hmac.new(secret, user_id.encode(), hashlib.sha256).hexdigest(), PHP is hash_hmac('sha256', $userId, $secret), Ruby is OpenSSL::HMAC.hexdigest('sha256', secret, user_id). If you send an email instead of a user id, hash the email.

Verified contacts show a check mark in the contact panel, so your team can tell at a glance whether the identity came from your server or from someone typing.

Where identify belongs in a Next.js app

The cleanest place is the root layout, as a server component that reads the session and passes a plain object down.

import { SvellyoWidget } from "@svellyo/sdk/react";
import { auth } from "@/lib/auth";
import { svellyoHash } from "@/lib/svellyo";

export default async function RootLayout({ children }) {
  const session = await auth();
  const user = session
    ? {
        userId: session.user.id,
        email: session.user.email,
        name: session.user.name,
        hash: svellyoHash(session.user.id),
        attributes: { plan: session.user.plan, role: session.user.role },
      }
    : null;
  return (
    <html>
      <body>
        {children}
        <SvellyoWidget workspaceId="ws_your_id" user={user} />
      </body>
    </html>
  );
}

The hash is computed on the server on each render, which is cheap, and never leaks the secret. When the session ends and user becomes null, the component resets the visitor.

What identity does not do

It does not merge across workspaces. A user identified on product A is not recognised on product B, even with the same user id, because workspaces are isolated by design. Identify on each product separately.

It does not replace your own auth. Svellyo trusts the hash, and the hash is only as trustworthy as the server that produced it. If your session handling is sound, so is your chat identity.

It does not backfill. Conversations a person had while anonymous stay anonymous unless the same browser identifies later, in which case that browser's visitor is merged into the contact.

The short version

Call identify with a stable user id, an email and a name. Add the attributes that change what the right answer is, especially plan. Sign the user id on your server and turn on verified identity, so the console cannot impersonate anyone. Reset on logout. Ten lines of code, and every conversation in your inbox starts with "Jane, Pro plan, admin" instead of "Visitor".