Anyone can type an e-mail address into a chat. Before your team or the AI agent changes an order, a delivery address or an account, the visitor should prove the address is theirs. Luxivo does this in the chat itself, on every plan.
A code by e-mail
- Someone asks the visitor to verify: an agent with Ask to verify in the inbox (the
Verification section next to the conversation), the AI agent by itself when the visitor asks
about their own order or account, or an AI app with the MCP tool
request_verification. - A card appears in the chat. The visitor sends a 6-digit code to the e-mail they left, or to another address.
- They type the code in the card, or click Confirm my e-mail in the code e-mail. The inbox shows a green Verified badge, and the chat gets a line saying the e-mail was verified.
The rules:
- A code works for 10 minutes and for 5 tries. A new code retires the old one and its link.
- A new code can be sent a minute after the last one, and at most 5 times an hour to one visitor and to one address, so nobody can flood someone else’s inbox from your chat.
- Only a hash of the code is stored. Neither the code nor its e-mail enters the conversation. If the visitor types the code into the message box instead of the card, it goes to the card.
- A verification lasts 24 hours for that visitor (that browser), or until their e-mail changes.
- The code e-mail uses the widget’s e-mail look (E-mails in the widget settings) and the visitor’s language.
- Website chats only: conversations that came by e-mail or a contact form have no widget to type a code in.
Orders need a verified e-mail
If your site reports the visitor’s cart and orders (the WooCommerce plugin, or
Luxivo("context", …)), the AI agent can answer questions about them. Switch on Orders need a
verified e-mail in the widget’s Security section and it sees them only after the visitor
verified. Until then it asks for the code. What the visitor says in the chat (“I already verified”)
changes nothing: the status comes from the server.
Signed identify for logged-in users
When your website already knows who is logged in, there’s no need for a code. Create an identity
secret in the widget’s Security section, sign the user’s e-mail on your server and pass the
signature to identify:
// Server (Node). Keep the secret on the server.
import { createHmac } from "node:crypto";
const signature = createHmac("sha256", process.env.LUXIVO_IDENTITY_SECRET)
.update(user.email.trim().toLowerCase())
.digest("hex");
// Server (PHP)
$signature = hash_hmac('sha256', strtolower(trim($user->email)), getenv('LUXIVO_IDENTITY_SECRET'));
<script>
Luxivo("identify", { email: "jane@example.com", name: "Jane", signature: "<?= $signature ?>" });
</script>
The signature is the hex HMAC-SHA256 of the lowercased e-mail. With a valid one, the visitor counts as verified (the inbox says “by your website”) and stays so while they browse, because every page view signs again. A missing or wrong signature is ignored: the e-mail is saved but not verified. Create a new secret replaces the old one, and signatures made with it stop counting.
Verified actions (Pro)
Verified actions let the AI agent and your team do things in your own systems: change a delivery
address, cancel an order that hasn’t shipped, resend an invoice. Set them up in Verified
actions (/app/actions):
- Name and When to use it: what the AI reads to decide when the action fits.
- Fields: what the action needs, for example
order_numberandaddress. - Webhook URL: your endpoint (https).
- The AI agent may run it: off means only your team can, from the inbox.
An action runs only for a visitor verified in the last 30 minutes. This is checked by Luxivo, not left to the AI. The AI confirms the details with the visitor before it runs one, and every run is an internal note in the conversation and an entry in the audit log.
Luxivo POSTs JSON to your URL:
{
"type": "action.run",
"action": { "id": "…", "name": "Change delivery address" },
"fields": { "order_number": "1042", "address": "Main St 1, 00-001 Warsaw" },
"visitor": { "id": "…", "email": "jane@example.com", "name": "Jane", "verifiedAt": "2026-10-27T14:02:11.000Z", "method": "code" },
"conversationId": "…",
"requestedBy": { "type": "ai" },
"sentAt": "2026-10-27T14:03:40.000Z"
}
method is code, link or signed. The request is signed like form webhooks:
X-Luxivo-Signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body>
with the action’s signing secret (shown when you edit the action). Check it and reject old
timestamps (see Contact forms for code).
Your endpoint must still check that the order belongs to visitor.email. Answer within 10 seconds
with a 2xx status when it’s done, and optionally { "message": "Done. The new address is on order 1042." }:
the AI or the agent passes it on. Any other status counts as failed, and the AI hands the
conversation to your team.
AI apps (MCP)
request_verification shows the card in a conversation, get_conversation returns
visitor.verifiedAt and the latest verification, and list_actions and run_action run verified
actions as the connected user. See Claude and ChatGPT.