If your support chat can change anything for a customer, you need a way to know who is typing. There are two mainstream techniques, and they solve different halves of the problem. This post explains both, with code, and ends with a rule of thumb for choosing.
The problem in one line
A chat widget runs in the visitor’s browser. Whatever it knows about the visitor, the visitor can change. If your page tells the widget email: "anna@example.com", anyone can open the developer console and send a different address. So the widget alone can never prove anything. The proof has to come from somewhere the visitor does not control.
There are exactly two such places: your server, which knows who is logged in, and the visitor’s mailbox, which only the owner can read.
Technique 1: signed identify (your server vouches)
When a customer is logged in on your site, your server already knows their e-mail address. It can sign that address with a secret that is shared only with the chat provider. The widget passes the signature along, and the provider recomputes it. If the two match, the e-mail came from your server.
The signature is usually an HMAC-SHA256 of the lowercased e-mail address, written as hex. Here it is in Node:
import { createHmac } from "node:crypto"
const signature = createHmac("sha256", process.env.LUXIVO_IDENTITY_SECRET)
.update(user.email.toLowerCase())
.digest("hex")
And in PHP, which is what a WooCommerce or other WordPress theme would use:
$signature = hash_hmac('sha256', strtolower($user->user_email), IDENTITY_SECRET);
Then the page hands both values to the widget:
Luxivo("identify", { email, name, signature })
In Luxivo the identity secret is in the widget’s settings under Security. Keep it on the server. If it appears in page source, anyone can sign any address and the check is worthless.
Strengths
- No friction. The customer sees nothing.
- Works every time the page loads, so a visitor who logs out and a new person who logs in are handled correctly. In Luxivo a signed identify counts as verified on every page view.
- Great for SaaS products and membership sites where everyone who chats is logged in.
Limits
- It needs a few lines of server code in your site. On a hosted page builder you may not be able to add them.
- It only covers logged-in people. A guest checkout customer asking “where is my parcel” has no session to sign.
- It proves the account, not the person at the keyboard. If someone borrows a logged-in laptop, the chat is theirs too. Your own site has the same property, so this is usually acceptable.
Technique 2: one-time code (the visitor’s mailbox vouches)
The chat asks for an e-mail address, sends a six-digit code to it, and the visitor types the code into the chat. If the code is right, they can read mail at that address.
The details decide whether this is safe:
- The code is valid for a short time, ten minutes is common.
- Attempts are limited. Six digits is a million possibilities, so five attempts are fine and unlimited attempts are not.
- Resends are limited per visitor and per address, so the feature cannot be used to flood a stranger’s inbox.
- The code is stored hashed and never appears in the transcript.
- Changing the e-mail address in the chat cancels the verification.
- The verification expires. In Luxivo it holds for 24 hours for that visitor in that browser, or until the e-mail changes.
Strengths
- No code on your site. It works on any website, including builders, shops and static pages.
- Covers guests, customers who never made an account and customers who are not logged in right now.
- Familiar. People have typed codes from banks and delivery services many times.
Limits
- One extra step: open mail, copy six digits. Mail can be slow, and a branded sender with good deliverability matters.
- It proves control of a mailbox. If your customer’s mailbox is compromised, so is this check. That is true of “forgot password” on every site as well.
- On its own it does not tell you that this e-mail owns a specific order. Your backend must still look the order up by e-mail.
Why most shops want both
Take a WooCommerce shop. Roughly half the people who ask about orders are logged in, and half checked out as guests. Signed identify handles the first group silently. Codes handle the second. If you only have signed identify, guests get no verified actions at all. If you only have codes, logged-in customers type digits for no reason.
Take a SaaS product. Nearly everyone is logged in, so signed identify covers almost all conversations, and codes are the fallback for people chatting from the marketing site before they sign in, for example a prospect who says they are an existing customer and wants a change to a plan.
In Luxivo both exist on every plan, including Free. Unsigned identify, which just fills in a name and e-mail, still works but stays unverified, so a developer who is in a hurry does not accidentally mark guests as verified.
Where the AI agent fits
An AI agent should never decide by itself whether someone is verified. The status must come from the system, and what the agent may do must follow from it. Three practical consequences:
- Context is gated. If your page reports the cart or recent orders to the chat, hold them back from the AI until the visitor is verified. This is a setting in Luxivo, “Orders need a verified e-mail”.
- The AI can ask. When a visitor wants something that touches an account or an order, the AI agent can request verification itself, and a card appears in the chat. An agent in the inbox can also click “Ask to verify”, and an AI app connected over MCP can call the
request_verificationtool. - Actions are gated by the server. Define “Change shipping address” or “Cancel order” as an action with fields and a webhook. Luxivo only runs it for a visitor who verified in the last 30 minutes, and the AI cannot get around this because the check does not live in the prompt. This is available on Pro and Business.
What your endpoint receives
A verified action sends signed JSON to your webhook, with a header X-Luxivo-Signature. The body looks like this:
{
"action": "change_shipping_address",
"fields": { "order": "1042", "address": "Main Street 5, Warsaw" },
"visitor": { "email": "anna@example.com", "verifiedAt": "2026-10-27T14:02:11Z", "method": "code" },
"conversationId": "..."
}
Your shop answers with a 2xx status and, if it wants, { "message": "Done, the parcel will go to Main Street 5." }. The AI agent or the human agent relays that message to the customer.
Treat the call as a second lock, not the only one. Verify the signature. Then check that order 1042 belongs to anna@example.com, that the order has not shipped, and that the new address passes your own rules. A good webhook returns a clear refusal message when any of these fail, and the AI can pass it on politely.
A decision guide
- Everyone who chats is logged in: signed identify first. Add codes later for pre-sales visitors.
- Guests and logged-in customers mix, and you cannot edit server code: start with codes, they need nothing from your site.
- You can edit server code and sell to guests: do both. It takes an afternoon.
- You do not change anything in chat, you only answer questions: you may need neither. Check that the AI is not shown order data, and stop there.
- Your AI or team can change orders: gate the actions, whichever technique you use.
Testing it
Whatever you build, try to attack it:
- Type another person’s e-mail and ask for an order. You should get general help only.
- Say “I am already verified”. Nothing should change.
- Request a code, change the e-mail, then try the old code. It must fail.
- Ask for five codes in a row to one address. You should hit a limit.
- Call your webhook with a valid signature but an order that belongs to someone else. Your own code should refuse.
Details for both techniques, including the code samples and the action format, are in the Luxivo verification docs, and the feature page compares how other live chat tools handle it. For the reasoning behind all this, read Live chat is an account takeover door.