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

FieldTypeRequiredNotes
systemNamestringyesMust belong to this tenant
companyCodestringwhen logged inDo not cast to int
userCodenumber or stringwhen logged inOmit for anonymous
userName, userEmail, userPhone, userLocalestringnoOptional profile
jtistringyesFresh UUID per mint
expunix intyesnow + 300 recommended; max horizon ~10 minutes
v3yesAlways 3

Authenticated vs anonymous

  • Authenticated: include systemName, companyCode, userCode (+ optional profile).
  • Anonymous: omit userCode entirely — no 0 / 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.