Guide · developers
Personal data for logged-in visitors
Let your assistant answer questions like "what did I book?" or "what's the status of my order?" — by querying your own API on behalf of the visitor who is logged in on your site.
The approach is deliberately simple and secure: your website tells the chatbot who is logged in, but does so signed, so a visitor can never pass themselves off as someone else. You'll have this up and running in a quarter of an hour.
Step 1 — Turn on verification and generate a secret
Open your bot in the portal, go to the Actions tab and switch on "Personal data of logged-in visitors". Click Generate for a shared secret and save.
- Keep the secret like a password — in an environment variable or your secrets store, never in your front-end code.
- Suspect it's leaked? Generate a new secret; the old signatures then become invalid straight away.
Step 2 — Compute the signature on your server
For the logged-in visitor, compute HMAC-SHA256(secret, user-id) and pass the result as hexadecimal text. This always happens server-side — the secret must not get into the browser.
<?php
// The secret stays safe on your server (from the portal). Never in the browser.
$secret = getenv('CODEBROUWERIJ_CHATBOT_SECRET');
$userId = (string) $currentUser->id;
// Sign the user ID.
$userHash = hash_hmac('sha256', $userId, $secret);
?>
<script src="https://aqivo.chat/embed.js"
data-bot-id="JOUW-BOT-ID"
data-user-id="<?= htmlspecialchars($userId) ?>"
data-user-hash="<?= $userHash ?>"
data-user-email="<?= htmlspecialchars($currentUser->email) ?>"
async></script>
const crypto = require('crypto');
// Server-side, on your logged-in route:
const secret = process.env.CODEBROUWERIJ_CHATBOT_SECRET;
const userId = String(req.user.id);
const userHash = crypto.createHmac('sha256', secret).update(userId).digest('hex');
// Pass userId + userHash to your view and render there:
// <script src="https://aqivo.chat/embed.js"
// data-bot-id="JOUW-BOT-ID"
// data-user-id="${userId}" data-user-hash="${userHash}" async></script>
@using System.Security.Cryptography
@using System.Text
@{
var secret = Configuration["CodebrouwerijChatbot:Secret"];
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var userHash = Convert.ToHexString(
hmac.ComputeHash(Encoding.UTF8.GetBytes(userId))).ToLowerInvariant();
}
<script src="https://aqivo.chat/embed.js"
data-bot-id="JOUW-BOT-ID"
data-user-id="@userId"
data-user-hash="@userHash"
async></script>
import hmac, hashlib
from django.conf import settings
# E.g. as a context processor or in your view:
def chatbot_context(request):
secret = settings.CODEBROUWERIJ_CHATBOT_SECRET.encode()
user_id = str(request.user.id)
user_hash = hmac.new(secret, user_id.encode(), hashlib.sha256).hexdigest()
return {"cb_user_id": user_id, "cb_user_hash": user_hash}
# In your template:
# <script src="https://aqivo.chat/embed.js"
# data-bot-id="JOUW-BOT-ID"
# data-user-id="{{ cb_user_id }}" data-user-hash="{{ cb_user_hash }}" async></script>
demo-secret and user ID user-42, the signature should be exactly this:
0e5cda9a37bbbac18483cc531c37c0fcd29ad35ba29fe5f1a120979a3d7ead8f
If you get the same, your code is right. (Uppercase or spaces? Make sure you return lowercase hex.)
Step 3 — Place the widget on your logged-in pages
On pages where the visitor is logged in, you render the widget with data-user-id and data-user-hash. On public pages you simply leave those attributes out — there the chatbot works anonymously.
data-user-id— the user ID from your system (gets verified).data-user-hash— the signature from step 2.data-user-email/data-user-name— optional, purely for info for a personal greeting.
Step 4 — Configure an action
In the portal (the Actions tab), add an action, for example get_reservations. You tell the assistant what the action does and which API it may call. In the URL, headers or body you use placeholders that we fill in server-side from the verified visitor:
{{user.id}}— the verified user ID{{user.email}}— the email address, if supplied{{user.token}}— a token that may have been passed along (see below){{naam}}— a parameter the assistant fills in itself (e.g. an order number)
An example URL for "my reservations":
https://jouwsite.nl/api/me/reservations?uid={{user.id}}
Important: your API must itself check that the requested data really belongs to this user. We supply the verified identity; the authorisation stays with you — that way it's double-locked.
Alternative: passing a token
Prefer to pass along an existing token (for example a short-lived JWT your API can already validate)? Then set data-user-token on the widget. We send that token along to your API (for example as Authorization: Bearer {{user.token}}), which derives the user from it itself. Use a short lifetime and the right audience.
Security in brief
- The secret lives only on your server. Never put it in HTML, JavaScript or a public repo.
- The signature is computed per visitor server-side — not in the browser.
- We compare the signature in constant time and refuse personal actions the moment it's wrong or missing.
- Actions run on our server with a short time-out and a block on internal addresses; your API keys stay out of the visitor's sight.
- Personal actions are available from the Crew plan onwards.
Do I have to compute a new signature for each page?
You compute it per logged-in visitor when the page is rendered. The signature belongs to the user ID, so as long as that stays the same, the signature stays valid. If you change your secret, they're refreshed automatically on the next page view.
What happens if the signature doesn't match?
Then the chatbot treats the visitor as anonymous: personal actions are refused and the assistant politely asks them to log in first. Ordinary questions are answered as usual.
Does this also work without the visitor ID, with just a token?
Yes. In that case pass data-user-token; your own API validates the token and determines the user. Handy if you already work with JWTs or session tokens.
Which API systems are supported?
Any HTTP/JSON API. You set the method, URL, headers and optionally a body. You handle authentication with a header (for example an API key) or via the passed token.
Ready to connect?
Switch on personalisation and add your first action in the portal.
Go to the portal