Authentication
Partners never implement HMAC themselves. You present your long-lived widget token plus a visitor payload; Mimmo stores the session and returns an opaque signature.
Envelope
innerPayload = base64url( JSON.stringify(payload) )
innerToken = base64url( BOT_TENANT_TOKEN )
data = base64url( innerPayload + "." + innerToken )
POST {BOT_API_URL}/api/chat/token
Body: { "data": "<data>" }
→ { "signature": "<opaque>" }Inner payload fields
| Field | Type | Required | Notes |
|---|---|---|---|
systemName | string | yes | Must belong to this tenant |
companyCode | string | when logged in | Do not cast to int |
userCode | number or string | when logged in | Omit for anonymous |
userName, userEmail, userPhone, userLocale | string | no | Optional profile |
jti | string | yes | Fresh UUID per mint |
exp | unix int | yes | now + 300 recommended; max horizon ~10 minutes |
v | 3 | yes | Always 3 |
Authenticated vs anonymous
- Authenticated: include
systemName,companyCode,userCode(+ optional profile). - Anonymous: omit
userCodeentirely — no0/ empty placeholders.
Code samples
Mint in your stack
<?php
function base64url_encode(string $data): string {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function getMimmoSessionSignature(
string $systemName,
?string $companyCode = null,
string|int|null $userCode = null,
?string $userName = null
): ?string {
$tenantToken = getenv('BOT_TENANT_TOKEN') ?: '';
if ($tenantToken === '') {
error_log('MIMMO: BOT_TENANT_TOKEN is empty');
return null;
}
$payload = [
'systemName' => $systemName,
'jti' => bin2hex(random_bytes(16)),
'exp' => time() + 300,
'v' => 3,
];
if ($companyCode !== null && $companyCode !== '') {
$payload['companyCode'] = (string) $companyCode; // never (int)
}
if ($userCode !== null && $userCode !== '') {
$payload['userCode'] = $userCode; // number or string OK
}
if ($userName) {
$payload['userName'] = $userName;
}
$innerPayload = base64url_encode(json_encode($payload, JSON_UNESCAPED_UNICODE));
$innerToken = base64url_encode($tenantToken);
$body = json_encode(['data' => base64url_encode($innerPayload . '.' . $innerToken)]);
$api = rtrim(getenv('BOT_API_URL') ?: 'https://bot.easygds.it', '/') . '/api/chat/token';
$ch = curl_init($api);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_TIMEOUT => 5,
]);
$response = curl_exec($ch);
$http = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http !== 200 || !$response) return null;
return json_decode($response, true)['signature'] ?? null;
}Extend
While the widget is open, it may call POST /api/chat/token/extend with the current session signature to keep the Redis TTL alive. Partners should still mint fresh on each full page load — see Widget embed.