PHPackages                             keyagency/kai-personalize - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. keyagency/kai-personalize

ActiveStatamic-addon

keyagency/kai-personalize
=========================

Adaptive content delivery based on visitor attributes and behavior

1.1.1(yesterday)02proprietaryPHPPHP ^8.1

Since Mar 20Pushed yesterdayCompare

[ Source](https://github.com/keyagency/kai-personalize)[ Packagist](https://packagist.org/packages/keyagency/kai-personalize)[ RSS](/packages/keyagency-kai-personalize/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (5)Versions (4)Used By (0)

Kai Personalize - Statamic Add-on
=================================

[](#kai-personalize---statamic-add-on)

[![Statamic Marketplace](https://camo.githubusercontent.com/c36e29bd3f64c42822c05fa01b9e3e1d348785f7eb86c94cfce03a867a0d22e0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53746174616d69632d4d61726b6574706c6163652d6f72616e67652e737667)](https://statamic.com/marketplace/addons/kai-personalize)[![Latest Version](https://camo.githubusercontent.com/9e57863444c8642b7887165e2fdded35ca30cc01a2da6227693403875b1af0a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d76312e312e312d626c75652e737667)](https://github.com/keyagency/kai-personalize/releases)

**Adaptive content delivery based on visitor attributes and behavior**

Overview
--------

[](#overview)

Kai Personalize is a professional Statamic add-on that enables you to deliver personalized content based on visitor attributes (browser, IP, location, device fingerprint) and external data sources (weather, news, etc.). Built by Key Agency with AI agent Kai.

Editions
--------

[](#editions)

Kai Personalize is available in two editions:

### Lite Edition

[](#lite-edition)

Perfect for getting started with personalization:

- Visitor tracking &amp; sessions
- Basic rules (max 5 active)
- Geolocation (MaxMind)
- API connections (max 2)
- Behavioral tracking (page views, scroll, clicks)

### Pro Edition

[](#pro-edition)

Advanced features for growing businesses:

- **Everything in Lite**
- Unlimited rules &amp; API connections
- Analytics dashboard &amp; engagement scoring
- Dynamic segments
- ActiveCampaign integration
- Data export functionality

**Upgrade to Pro** at [statamic.com/marketplace/addons/kai-personalize](https://statamic.com/marketplace/addons/kai-personalize)

### Feature Comparison

[](#feature-comparison)

FeatureLiteProVisitor Tracking✅✅Session Management✅✅Browser Detection✅✅Geolocation (MaxMind)✅✅API Connections2 maxUnlimitedPersonalization Rules5 maxUnlimitedBehavioral Tracking✅✅Analytics Dashboard❌✅Engagement Scoring❌✅Dynamic Segments❌✅ActiveCampaign Integration❌✅Data Export❌✅Current Status
--------------

[](#current-status)

**Version:** v1.1.1 - Production Ready **Status:** All core features complete and functional. Now compatible with Statamic 6!

### ✅ What's Working Now:

[](#-whats-working-now)

- ✅ Visitor tracking (server-side)
- ✅ Session management
- ✅ Database structure (12 tables)
- ✅ All Antlers tags
- ✅ API services (Weather, Geolocation, Custom)
- ✅ Artisan commands
- ✅ **Complete Control Panel Interface:**
    - ✅ Dashboard with real-time statistics
    - ✅ **Analytics &amp; Engagement Scoring** (NEW)
    - ✅ Rules management (CRUD, condition builder, statistics)
    - ✅ Visitors management (browse, search, profiles, sessions, page history, behavioral summary)
    - ✅ Segments (CRUD, visitor assignment, criteria builder)
    - ✅ API Connections (CRUD, testing, cache management)
    - ✅ Settings page (configuration overview)
- ✅ Privacy features (IP encryption, DNT, GDPR compliance)

### 🔨 Next Up:

[](#-next-up)

- Enhanced dashboard with charts
- Export/Import functionality
- Segment-based condition support in Rules

Features
--------

[](#features)

- **Browser Detection**: Comprehensive browser, device, and bot detection via [jenssegers/agent](https://github.com/jenssegers/agent)
- **Local Geolocation**: Fast IP-to-location lookups using [MaxMind GeoIP2](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data) local databases (no API calls)
- **Browser Fingerprinting**: Advanced visitor identification using canvas, WebGL, audio, and more
- **Session Management**: Leverages Statamic's built-in session system with visitor tracking
- **Behavioral Tracking**: Monitor page views, time on site, referrers, and UTM parameters
- **External API Integration**: Connect to weather, news, exchange rates, and custom APIs
- **ActiveCampaign Integration**: Automatic email campaign visitor tracking and CRM data sync
- **Rule-Based Personalization**: Create complex conditions to show different content
- **Privacy Compliant**: GDPR support, IP encryption, DNT respect, and data anonymization
- **Multilingual**: Full English and Dutch support
- **Control Panel Interface**: Comprehensive dashboard for managing all aspects
- **Performance Optimized**: Caching, queueing, and batch operations

Dependencies
------------

[](#dependencies)

- **PHP**: ^8.2
- **Statamic**: ^6.0
- **jenssegers/agent**: ^2.6 - Browser/device detection
- **geoip2/geoip2**: ^3.0 - MaxMind GeoIP2 local database reader

Quick Start
-----------

[](#quick-start)

Once installed, you can immediately:

1. **View visitor data**: Go to `/cp/kai-personalize` in your Control Panel
2. **Use Antlers tags**: Add personalization to your templates (see examples below)
3. **Check settings**: Configure features at `/cp/kai-personalize/settings`

The addon automatically tracks visitors as they browse your site!

Installation
------------

[](#installation)

1. Add the addon to your project:

```
composer require keyagency/kai-personalize
```

2. Publish configuration and translations:

```
php artisan vendor:publish --tag=kai-personalize-config
php artisan vendor:publish --tag=kai-personalize-translations
```

3. Run migrations:

```
php artisan migrate
```

4. Configure your API keys in `.env`:

```
# External API keys
GEOLOCATION_API_KEY=your_key_here
WEATHER_API_KEY=your_key_here
NEWS_API_KEY=your_key_here
EXCHANGE_API_KEY=your_key_here

# ActiveCampaign integration (optional)
KAI_ACTIVECAMPAIGN_ENABLED=true
KAI_ACTIVECAMPAIGN_URL=https://your-account.api-us1.com
KAI_ACTIVECAMPAIGN_API_KEY=your_api_key_here
KAI_ACTIVECAMPAIGN_COOKIE=vgo_ee
KAI_ACTIVECAMPAIGN_CACHE_TTL=1440

# Tracker Queue Settings (optional)
KAI_QUEUE_THRESHOLD=5
KAI_QUEUE_SEND_INTERVAL=20000
KAI_QUEUE_PERSIST=true
KAI_QUEUE_STORAGE_KEY=kai_tracker_queue
KAI_QUEUE_MAX_EVENT_AGE=3600000

# Security (optional but recommended for production)
KAI_TRACKING_SECRET=your-unique-secret-key-here
KAI_TRACKING_ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
```

5. (Optional) Set up MaxMind GeoIP2 for local geolocation:

```
# Create the geoip directory
mkdir -p storage/app/geoip

# Download free GeoLite2 databases from:
# https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
# Place the .mmdb files in storage/app/geoip/
```

Configuration
-------------

[](#configuration)

The configuration file is located at `config/kai-personalize.php`. Key settings include:

```
'features' => [
    'fingerprinting' => true,
    'ip_tracking' => true,
    'geolocation' => true,
    'behavioral_tracking' => true,
    'external_data' => true,
],

'privacy' => [
    'encrypt_ip' => true,
    'anonymize_after_days' => 30,
    'respect_dnt' => true,
    'gdpr_mode' => false,
],

// MaxMind GeoIP2 Local Database (no API calls needed)
'maxmind' => [
    'enabled' => true,
    'database_city' => 'app/geoip/GeoLite2-City.mmdb',
    'database_country' => 'app/geoip/GeoLite2-Country.mmdb',
    'database_asn' => 'app/geoip/GeoLite2-ASN.mmdb',
    'cache_duration' => 86400,
],
```

MaxMind GeoIP2 (Local Geolocation)
----------------------------------

[](#maxmind-geoip2-local-geolocation)

The addon uses MaxMind GeoIP2 for fast, local geolocation lookups without API calls.

### Setup

[](#setup)

1. Create a free MaxMind account at
2. Generate a license key in your MaxMind account
3. Download the databases using the Artisan command:

```
# Download all databases
php artisan kai:maxmind:download --license=YOUR_LICENSE_KEY

# Or set the license key in .env and run without option
# MAXMIND_LICENSE_KEY=your_license_key
php artisan kai:maxmind:download

# Download only specific database
php artisan kai:maxmind:download --database=city
php artisan kai:maxmind:download --database=country
php artisan kai:maxmind:download --database=asn
```

**Available databases:**

- **GeoLite2-City.mmdb** - Full location data (country, region, city, postal, timezone, coordinates)
- **GeoLite2-Country.mmdb** - Country only (smaller, faster)
- **GeoLite2-ASN.mmdb** - ISP/organization info (optional)

The command downloads and extracts the `.mmdb` files to `storage/app/geoip/`

### Configuration

[](#configuration-1)

```
# Enable/disable MaxMind
KAI_MAXMIND_ENABLED=true

# Database paths (relative to storage/)
KAI_MAXMIND_CITY_DB=app/geoip/GeoLite2-City.mmdb
KAI_MAXMIND_COUNTRY_DB=app/geoip/GeoLite2-Country.mmdb
KAI_MAXMIND_ASN_DB=app/geoip/GeoLite2-ASN.mmdb

# Cache duration in seconds (default: 24 hours)
KAI_MAXMIND_CACHE=86400

# Optional: License key for automatic updates
MAXMIND_LICENSE_KEY=your_license_key
```

### Stored Attributes

[](#stored-attributes)

When enabled, MaxMind stores these visitor attributes (type: `external`):

AttributeExampleDatabase`country`NetherlandsCity/Country`country_code`NLCity/Country`region`North HollandCity`region_code`NHCity`city`AmsterdamCity`postal_code`1012City`continent`EuropeCity/Country`continent_code`EUCity/Country`timezone`Europe/AmsterdamCity`is_eu`1City/Country`latitude`52.3676City`longitude`4.9041City`isp`KPN B.V.ASN> **Note:** Coordinates are not stored when `gdpr_mode` is enabled.

ActiveCampaign Integration
--------------------------

[](#activecampaign-integration)

The addon integrates with ActiveCampaign to automatically identify visitors from email campaigns and personalize content based on their CRM data.

### How It Works

[](#how-it-works)

1. User clicks link in ActiveCampaign email → lands on site with tracking cookie (`vgo_ee`, `__actc`, etc.)
2. TrackVisitor middleware detects the AC cookie
3. Server-side API call fetches contact data (tags, lists, custom fields)
4. Data stored as visitor attributes (type: `crm`)
5. Available via `{{ kai:visitor }}` tag for personalization

### Configuration

[](#configuration-2)

Add these settings to your `.env` file:

```
# Enable ActiveCampaign integration
KAI_ACTIVECAMPAIGN_ENABLED=true

# ActiveCampaign API credentials
KAI_ACTIVECAMPAIGN_URL=https://your-account.api-us1.com
KAI_ACTIVECAMPAIGN_API_KEY=your_api_key_here

# Cookie name that contains the email (ActiveCampaign default)
KAI_ACTIVECAMPAIGN_COOKIE=vgo_ee

# Cache duration in minutes (default: 1440 = 24 hours)
KAI_ACTIVECAMPAIGN_CACHE_TTL=1440
```

### Stored Attributes

[](#stored-attributes-1)

When a visitor arrives from an ActiveCampaign email, these attributes are stored (type: `crm`):

AttributeTypeDescription`ac_contact_id`stringActiveCampaign contact ID`ac_email`stringContact email address`ac_first_name`stringFirst name`ac_last_name`stringLast name`ac_phone`stringPhone number`ac_tags`jsonArray of tag names`ac_lists`jsonObject with list membership status`ac_custom_fields`jsonCustom field values`ac_created_at`timestampAccount created date`ac_updated_at`timestampLast updated in AC### Usage Examples

[](#usage-examples)

#### Personalize by Tag

[](#personalize-by-tag)

```
{{ kai:visitor }}
    {{ if ac_tags contains 'VIP' }}
        Welcome back, VIP member! Here's your exclusive content.
    {{ /if }}
{{ /kai:visitor }}
```

#### Personalize by List Membership

[](#personalize-by-list-membership)

```
{{ kai:visitor }}
    {{ if ac_lists.newsletter.status == 1 }}
        Thanks for being a subscriber!
    {{ /if }}
{{ /kai:visitor }}
```

#### Personalize by Custom Field

[](#personalize-by-custom-field)

```
{{ kai:visitor }}
    {{ if ac_custom_fields.member_level == 'Gold' }}
        Gold member exclusive benefits
    {{ /if }}
{{ /kai:visitor }}
```

#### Condition Tag with AC Data

[](#condition-tag-with-ac-data)

```
{{ kai:condition attribute="ac_member_level" operator="equals" value="Gold" }}
    Gold member exclusive content
{{ /kai:condition }}
```

### Testing

[](#testing)

Test the ActiveCampaign integration via command line:

```
# Test API connection
php artisan kai:test-activecampaign

# Test email lookup
php artisan kai:test-activecampaign --email=user@example.com

# Test cookie-based retrieval (interactive)
php artisan kai:test-activecampaign --test-cookie
```

### Cookie Decoding

[](#cookie-decoding)

The service automatically handles multiple ActiveCampaign cookie encoding formats:

- Base64 encoded email
- URL-encoded + Base64
- Plain text email
- URL-encoded email

The default cookie name is `vgo_ee` but can be configured via `KAI_ACTIVECAMPAIGN_COOKIE`. Alternative cookies (`__actc`, `contact_email`) are checked as fallbacks.

### Privacy &amp; GDPR

[](#privacy--gdpr)

1. **Cookie consent** - Only reads AC cookie if consent given (when `cookie_consent_required` is enabled)
2. **Data retention** - Cache TTL respects AC rate limits (default 24 hours)
3. **Right to be forgotten** - AC attributes are cleared when visitor data is deleted
4. **Logging** - API calls are logged but sensitive data is masked

Browser Detection
-----------------

[](#browser-detection)

The addon uses [jenssegers/agent](https://github.com/jenssegers/agent) for comprehensive browser and device detection.

### Stored Attributes

[](#stored-attributes-2)

These attributes are automatically stored for each visitor (type: `technical`):

AttributeExampleDescription`browser`ChromeBrowser name`browser_version`120.0.0.0Full version string`browser_version_major`120Major version number`platform`OS XOperating system`platform_version`10\_15\_7OS version`device`MacintoshDevice name`device_type`desktopmobile, tablet, or desktop`is_mobile`0Is mobile device (includes tablets)`is_tablet`0Is tablet`is_desktop`1Is desktop`is_phone`0Is phone (mobile but not tablet)`is_bot`0Is bot/crawler`bot_name`GooglebotBot name (if detected)`accepted_languages`en,nlFrom Accept-Language header### Conditional Content by Browser/Device

[](#conditional-content-by-browserdevice)

```
{{# Show different content for mobile users #}}
{{ kai:condition attribute="is_mobile" operator="equals" value="1" }}
    Call us
{{ /kai:condition }}

{{# Target specific browsers #}}
{{ kai:condition attribute="browser" operator="equals" value="Safari" }}
    You're using Safari!
{{ /kai:condition }}

{{# Hide content from bots #}}
{{ kai:condition attribute="is_bot" operator="equals" value="0" }}
    ...
{{ /kai:condition }}
```

Antlers Tags
------------

[](#antlers-tags)

### kai:visitor

[](#kaivisitor)

Get information about the current visitor:

```
{{ kai:visitor }}
    {{# Basic info #}}
    {{ fingerprint }}
    {{ session_id }}
    {{ ip_address }}
    {{ visit_count }}
    {{ is_returning }}
    {{ first_visit }}
    {{ last_visit }}

    {{# Browser info (via jenssegers/agent) #}}
    {{ browser }}              {{# Chrome, Firefox, Safari, Edge, etc. #}}
    {{ browser_version }}      {{# Full version: 120.0.0.0 #}}
    {{ browser_version_major }} {{# Major version: 120 #}}

    {{# Platform/OS info #}}
    {{ platform }}             {{# Windows, OS X, Linux, Android, iOS #}}
    {{ platform_version }}     {{# OS version #}}

    {{# Device info #}}
    {{ device }}               {{# iPhone, iPad, Macintosh, etc. #}}
    {{ device_type }}          {{# mobile, tablet, desktop #}}
    {{ is_mobile }}            {{# true/false #}}
    {{ is_tablet }}            {{# true/false #}}
    {{ is_desktop }}           {{# true/false #}}
    {{ is_phone }}             {{# true/false (mobile but not tablet) #}}

    {{# Bot detection #}}
    {{ is_bot }}               {{# true/false #}}
    {{ bot_name }}             {{# Googlebot, bingbot, etc. #}}

    {{# Geolocation (via MaxMind) #}}
    {{ country }}
    {{ country_code }}
    {{ region }}
    {{ city }}
    {{ postal_code }}
    {{ timezone }}
    {{ continent }}
    {{ is_eu }}

    {{# Traffic source #}}
    {{ referrer }}
    {{ utm_source }}
    {{ utm_medium }}
    {{ utm_campaign }}

    {{# Languages #}}
    {{ language }}
    {{ accepted_languages }}

    {{# ActiveCampaign (if enabled and visitor from email) #}}
    {{ ac_contact_id }}
    {{ ac_email }}
    {{ ac_first_name }}
    {{ ac_last_name }}
    {{ ac_phone }}
    {{ ac_tags }}            {{# Array of tag names #}}
    {{ ac_lists }}           {{# Object with list status #}}
    {{ ac_custom_fields }}   {{# Custom field values #}}
    {{ ac_created_at }}
    {{ ac_updated_at }}
{{ /kai:visitor }}
```

### kai:condition

[](#kaicondition)

Show content based on conditions:

```
{{ kai:condition attribute="country" operator="equals" value="US" }}
    Content for US visitors
{{ /kai:condition }}

{{ kai:condition attribute="device_type" operator="equals" value="mobile" }}
    Mobile-specific content
{{ /kai:condition }}

{{ kai:condition attribute="visit_count" operator="greater_than" value="5" }}
    Welcome back, loyal visitor!
{{ /kai:condition }}
```

### kai:external

[](#kaiexternal)

Fetch data from external APIs:

```
{{# Weather API #}}
{{ kai:external source="weather" location="Amsterdam" }}
    Temperature: {{ temperature }}°C
    Condition: {{ condition }}
{{ /kai:external }}

{{# Geolocation #}}
{{ kai:external source="geolocation" }}
    You are in {{ city }}, {{ country }}
{{ /kai:external }}

{{# Custom API #}}
{{ kai:external
    source="custom"
    connection="my-api"
    endpoint="/data"
    params:id="123"
}}
    {{ response_data }}
{{ /kai:external }}
```

### kai:content

[](#kaicontent)

Display content based on rules:

```
{{ kai:content rules="homepage-hero" fallback="default" }}
    {{ if condition_met }}
        Personalized Hero Content
    {{ else }}
        Default Hero Content
    {{ /if }}
{{ /kai:content }}
```

### kai:session

[](#kaisession)

Manage session data:

```
{{# Set session data #}}
{{ kai:session:set key="preference" value="dark_mode" }}

{{# Get session data #}}
{{ kai:session:get key="preference" }}

{{# Check if visitor is tracked #}}
{{ if {kai:session:tracked} }}
    We know you!
{{ /if }}
```

### kai:api

[](#kaiapi)

Make direct API calls with caching:

```
{{ kai:api
    url="https://api.example.com/data"
    method="GET"
    cache="600"
    params:category="news"
}}
    {{ results }}
{{ /kai:api }}
```

### kai:track

[](#kaitrack)

Outputs the client-side tracking script for behavioral analytics. This enables automatic tracking of user interactions without requiring manual event tagging.

#### Usage

[](#usage)

Add to your main layout file (typically in `` or before closing ``):

```
{{! In your layout file, e.g., resources/views/layouts/layout.antlers.html }}

    {{! Other head content }}
    {{ kai:track }}

    {{! Or place it before closing body }}
    {{ kai:track }}

```

#### What It Tracks

[](#what-it-tracks)

When enabled, the tracker automatically captures:

FeatureDescription**Page Views**URL, title, referrer, screen dimensions**Scroll Depth**Thresholds: 25%, 50%, 75%, 90%, 100%**Reading Time**Active reading time per page**Clicks**All clicks with element, position, and hesitation time**Rage Clicks**3+ clicks on same element within 2 seconds**Dead Clicks**Clicks on non-interactive elements**Visibility**Page visibility changes (hidden/visible/pagehide)**Exit Intent**Mouse leaving viewport (potential exit)**Idle Detection**No activity for 60+ seconds**Device**Viewport, screen, touch, connection info**Preferences**Dark mode, reduced motion, language, timezone**Fingerprint**Browser fingerprint (canvas, WebGL, audio)#### Configuration

[](#configuration-3)

Features are controlled via `config/kai-personalize.php`:

```
'features' => [
    'scroll_tracking' => true,   // Scroll depth, reading time, exit intent
    'click_tracking' => true,   // Clicks, rage clicks, dead clicks, hesitation
    'form_tracking' => false,   // Form interactions
    'video_tracking' => false,  // Video engagement
    'fingerprinting' => true,   // Browser fingerprinting
],
```

#### Master Switch

[](#master-switch)

To completely disable tracking:

```
KAI_PERSONALIZE_ENABLED=false
```

Or in config:

```
'enabled' => env('KAI_PERSONALIZE_ENABLED', true),
```

When disabled, the tag outputs nothing.

#### Privacy

[](#privacy)

The tracker respects:

- **DNT header** - Stops tracking if `navigator.doNotTrack === '1'`
- **Cookie consent** - Checks for common consent cookie implementations
- **Custom consent callback** - Use `window.KaiConsentCallback` function for custom logic

#### JavaScript API

[](#javascript-api)

The tracker exposes a global API for manual control:

```
// Check if tracking is enabled
if (window.KaiTracker.hasConsent()) {
    // Manual event tracking
    window.KaiTracker.track('custom_event', { my_data: 'value' });

    // Force send queued events
    window.KaiTracker.send();
}
```

#### How It Works

[](#how-it-works-1)

1. The tag outputs a config script with visitor/session IDs
2. Loads `tracker.js` from `/kai-personalize/tracker.js`
3. Events are queued and batched (configurable threshold and interval)
4. Queue is persisted to localStorage (survives page refreshes)
5. Uses `sendBeacon` for reliable delivery on page unload
6. Cached for 1 day on the client

#### Queue Configuration

[](#queue-configuration)

The tracker uses intelligent queue management to ensure reliable event delivery:

SettingDefaultDescription`threshold`5 eventsAuto-send when queue reaches this size`sendInterval`20000 ms (20s)Periodic send interval`persistQueue`trueEnable localStorage persistence`storageKey``kai_tracker_queue`localStorage key name`maxEventAge`3600000 ms (1h)Maximum event age before discarding**Configure via `.env`:**

```
KAI_QUEUE_THRESHOLD=5              # Send after 5 events
KAI_QUEUE_SEND_INTERVAL=20000      # Send every 20 seconds
KAI_QUEUE_PERSIST=true             # Persist to localStorage
KAI_QUEUE_STORAGE_KEY=kai_tracker_queue
KAI_QUEUE_MAX_EVENT_AGE=3600000    # Discard events older than 1 hour
```

**localStorage Persistence:**

- Events are saved to localStorage as they're queued
- Survives page refreshes and navigation
- Automatically restored on page load
- Stale events (older than `maxEventAge`) are discarded
- Gracefully handles quota exceeded errors

### kai:behavior

[](#kaibehavior)

Get behavioral statistics for the current visitor:

```
{{ kai:behavior }}
    {{ max_scroll_depth }}
    {{ total_reading_time_ms }}
    {{ total_clicks }}
    {{ total_events }}
{{ /kai:behavior }}
```

### kai:tracking

[](#kaitracking)

Generates cryptographic signatures for secure tracking endpoint validation. **Only required when `KAI_TRACKING_SECRET` is configured.**

#### Usage

[](#usage-1)

When HMAC signature validation is enabled, use this tag to generate a signature for the tracking endpoint:

```
{{ kai:tracking }}
    {{ signature }}        {{! HMAC SHA-256 signature }}
    {{ nonce }}            {{! Unique nonce for replay protection }}
    {{ timestamp }}        {{! Current Unix timestamp }}
    {{ enabled }}          {{! Whether signature validation is enabled }}
{{ /kai:tracking }}
```

#### JavaScript Integration

[](#javascript-integration)

The signature must be included with tracking requests:

```
// Get signature from kai:tracking tag (mounted on window)
fetch(window.KaiTracking.endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        visitor_id: window.KaiConfig.visitorId,
        session_id: window.KaiConfig.sessionId,
        events: events,
        signature: window.KaiTracking.signature,
        nonce: window.KaiTracking.nonce,
        timestamp: window.KaiTracking.timestamp
    })
});
```

#### When to Use

[](#when-to-use)

- **Required** when `KAI_TRACKING_SECRET` is set in `.env`
- **Not needed** for basic tracking without signature validation
- The `{{ kai:track }}` tag automatically handles this internally

Installation - CSRF Exceptions
------------------------------

[](#installation---csrf-exceptions)

When using the tracking endpoint, you must add CSRF token exceptions to `bootstrap/app.php`:

```
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'kai-personalize/track',      // Main tracking endpoint
        'kai-personalize/*',          // All Kai Personalize routes
    ]);
})
```

This allows the tracking JavaScript to POST events without CSRF tokens.

API Connections
---------------

[](#api-connections)

### Built-in Providers

[](#built-in-providers)

The addon supports these built-in API providers:

**Weather APIs:**

- OpenWeatherMap
- WeatherAPI
- AccuWeather

**Geolocation APIs:**

- IPapi
- MaxMind GeoIP2
- IP2Location
- ipstack

### Custom API Connections

[](#custom-api-connections)

Manage your external API connections via the Control Panel:

1. Navigate to **Kai Personalize &gt; API Connections**
2. Click **Create Connection**
3. Fill in the details:
    - Name
    - Provider type (Weather, Geolocation, News, Exchange, Custom)
    - API URL
    - Authentication (None, API key, Bearer, Basic, OAuth2, Custom)
    - Rate limits
    - Cache duration
    - Custom headers (optional)
4. **Test** the connection to ensure it works
5. **View statistics**: Total requests, success rate, cache usage
6. **Manage cache**: Clear cached responses when needed

### Testing Connections

[](#testing-connections)

Test API connections from the command line:

```
php artisan kai:test-api connection-name
```

Artisan Commands
----------------

[](#artisan-commands)

```
# Download MaxMind GeoLite2 databases
php artisan kai:maxmind:download --license=YOUR_LICENSE_KEY
php artisan kai:maxmind:download --database=city  # Download only city database
php artisan kai:maxmind:download                  # Uses MAXMIND_LICENSE_KEY from .env

# Test MaxMind database lookup
php artisan kai:maxmind:test                      # Test with default IP (95.97.1.234)
php artisan kai:maxmind:test 8.8.8.8              # Test with specific IP
php artisan kai:maxmind:test --info               # Show database info only

# Clean up old visitor data
php artisan kai:cleanup --days=30

# Test an API connection
php artisan kai:test-api my-connection

# Refresh API cache
php artisan kai:refresh-cache --all
php artisan kai:refresh-cache connection-name

# Prune old API logs
php artisan kai:prune-logs --days=30

# Test ActiveCampaign integration
php artisan kai:test-activecampaign                # Test connection
php artisan kai:test-activecampaign --email=user@example.com  # Test email lookup
php artisan kai:test-activecampaign --test-cookie # Test cookie retrieval
```

Database Structure
------------------

[](#database-structure)

The addon uses the following tables (all prefixed with `kai_personalize_`):

- **visitors** - Unique visitors identified by fingerprint
- **visitor\_sessions** - Individual browsing sessions
- **visitor\_attributes** - Custom visitor attributes
- **page\_views** - Page view tracking with entry metadata
- **events** - Behavioral events (scroll depth, clicks, reading time, etc.)
- **rules** - Personalization rules and conditions
- **segments** - Visitor segments with criteria
- **segment\_visitor** - Pivot table for visitor-segment relationships
- **logs** - Personalization event logs
- **api\_connections** - External API configurations
- **api\_cache** - Cached API responses
- **api\_logs** - API request logs

Privacy &amp; GDPR
------------------

[](#privacy--gdpr-1)

The addon includes several privacy features:

- **IP Encryption**: Automatically encrypt stored IP addresses
- **Do Not Track**: Respect DNT browser headers
- **Data Anonymization**: Automatically anonymize data after specified period
- **GDPR Mode**: Additional privacy controls for EU compliance
- **Cookie Consent**: Optional cookie consent requirement
- **Data Retention**: Configurable retention periods for all data types
- **Right to be Forgotten**: Delete visitor data via CP or API

Tracking Security
-----------------

[](#tracking-security)

The addon includes multiple layers of protection to prevent data pollution and abuse:

### Built-in Protections

[](#built-in-protections)

ProtectionDescriptionDefault**Rate Limiting**Max 60 requests/minute, 500/hour per IP✅ Enabled**Input Sanitization**Event types validated, HTML stripped, whitelist keys✅ Enabled**Max Events**Maximum 50 events per request✅ Enabled**Event Type Regex**Only alphanumeric + underscore allowed✅ Enabled**HMAC Signatures**Cryptographic validation of tracking requests⚠️ Optional**Timestamp Validation**Rejects expired signatures (5 min)⚠️ Optional**Replay Protection**Nonce caching prevents duplicate requests⚠️ Optional**Origin Validation**Whitelist allowed domains⚠️ Optional### Enabling HMAC Signature Validation (Recommended for Production)

[](#enabling-hmac-signature-validation-recommended-for-production)

To enable cryptographic signature validation, configure a secret key:

```
# Generate a unique 32+ character string
KAI_TRACKING_SECRET=your-unique-secret-key-here

# Optional: Restrict to your domains (comma-separated)
KAI_TRACKING_ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com,*.yourdomain.com
```

### How Signature Validation Works

[](#how-signature-validation-works)

1. **Server generates signature** using the `{{ kai:tracking }}` tag:

    ```
    {{ kai:tracking }}
        signature: "abc123..."
        nonce: "def456..."
        timestamp: 1738492800
        enabled: true
    {{ /kai:tracking }}
    ```
2. **Client includes signature** with tracking requests:

    ```
    fetch('/kai-personalize/track', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            visitor_id: '...',
            session_id: '...',
            events: [...],
            signature: '...',    // From server
            nonce: '...',         // From server
            timestamp: 1738492800 // From server
        })
    });
    ```
3. **Server verifies** before processing:

    - Signature matches (HMAC SHA-256)
    - Timestamp is recent (within 5 minutes)
    - Nonce hasn't been used before

### Security Best Practices

[](#security-best-practices)

1. **Always use HTTPS** - Signatures can be intercepted over HTTP
2. **Generate a strong secret** - Use `php artisan key:generate --show`
3. **Set allowed origins** - Restricts cross-origin requests
4. **Monitor logs** - Failed signature attempts are logged
5. **Use a WAF** - CloudFlare or similar for DDoS protection

Performance
-----------

[](#performance)

The addon is optimized for performance:

- **Caching**: Redis/file-based caching for API responses and visitor data
- **Queueing**: Optional queue support for heavy operations
- **Batch Operations**: Efficient bulk inserts and updates
- **CDN-Friendly**: Static asset delivery
- **Rate Limiting**: Prevent API overuse
- **Circuit Breaker**: Graceful degradation when APIs fail

Multilingual Support
--------------------

[](#multilingual-support)

The addon is fully translated in:

- English (en)
- Dutch (nl)

All Control Panel text, error messages, and documentation are available in both languages. The addon automatically detects the current site locale.

Control Panel
-------------

[](#control-panel)

Access the Control Panel interface at `/cp/kai-personalize`:

### ✅ Fully Implemented:

[](#-fully-implemented)

- **Dashboard**: Overview of visitors, sessions, top pages, and top engaged visitors
- **Analytics**: Page-level analytics with views, unique visitors, scroll depth, and reading time
- **Rules**: Create and manage personalization rules with condition builder
- **Visitors**: Browse visitor profiles with engagement scores, page history, behavioral summary, sessions, and attributes
- **Segments**: Create dynamic visitor segments with criteria-based assignment
- **API Connections**: Manage external API integrations with testing and cache management
- **Settings**: Configure features, privacy, and performance

### Analytics &amp; Engagement Scoring

[](#analytics--engagement-scoring)

The addon now includes comprehensive analytics and engagement tracking:

#### Engagement Score (0-100)

[](#engagement-score-0-100)

Each visitor receives an engagement score based on:

- **Visit Frequency** (0-30 points): `visit_count × 3`, max 30
- **Page Views** (0-25 points): `page_views × 2`, max 25
- **Reading Time** (0-25 points): For every 10 seconds of reading time, 1 point, max 25
- **Scroll Depth** (0-20 points): Max scroll depth / 5, max 20

Color-coded badges:

- 🟢 Green (70-100): Highly engaged
- 🟡 Yellow (40-69): Moderately engaged
- ⚪ Gray (0-39): Low engagement

#### Behavioral Summary

[](#behavioral-summary)

For each visitor, track:

- **Max Scroll Depth**: Deepest scroll percentage recorded
- **Reading Time**: Total time spent reading (in minutes)
- **Total Clicks**: Number of click events tracked
- **Total Events**: All behavioral events combined

#### Page Analytics

[](#page-analytics)

Navigate to **Analytics &gt; Pages** to see:

- Total views per page
- Unique visitors per page
- First and last view timestamps
- Average scroll depth
- Average reading time
- Recent views with visitor links

#### Visitor Page History

[](#visitor-page-history)

Each visitor profile now includes:

- Complete browsing history with pagination
- Entry title and collection
- URL path
- View timestamp

### Available Event Types

[](#available-event-types)

The addon tracks these behavioral events via the `kai:track` tag:

- `scroll_depth` - Maximum scroll percentage on a page
- `click` - Click events on elements
- `visibility` - Element visibility tracking
- `reading_time` - Time spent reading content
- `custom` - Custom events

Example Use Cases
-----------------

[](#example-use-cases)

### Weather-Based Content

[](#weather-based-content)

```
{{ kai:external source="weather" }}
    {{ if condition == "Rain" }}
        Don't forget your umbrella! ☔
    {{ /if }}
{{ /kai:external }}
```

### Returning Visitor Welcome

[](#returning-visitor-welcome)

```
{{ kai:visitor }}
    {{ if is_returning }}
        Welcome back!
        This is visit #{{ visit_count }}
    {{ else }}
        Welcome!
        First time here?
    {{ /if }}
{{ /kai:visitor }}
```

### Location-Based Content

[](#location-based-content)

```
{{ kai:condition attribute="country" operator="equals" value="NL" }}
    Welkom! Dit is Nederlandse content.
{{ /kai:condition }}

{{ kai:condition attribute="country" operator="not_equals" value="NL" }}
    Welcome! This is international content.
{{ /kai:condition }}
```

### Device-Specific CTAs

[](#device-specific-ctas)

```
{{ kai:condition attribute="device_type" operator="equals" value="mobile" }}
    Call Us Now
{{ /kai:condition }}

{{ kai:condition attribute="device_type" operator="equals" value="desktop" }}
    Contact Us
{{ /kai:condition }}
```

Development
-----------

[](#development)

### Local Development Setup

[](#local-development-setup)

When developing the addon locally, use Composer's path repository to symlink the addon directory. Changes reflect instantly, and you commit in the addon repo separately.

#### Complete Setup

[](#complete-setup)

1. **Add path repository to your project's `composer.json`:**

```
{
    "repositories": [
        {
            "type": "path",
            "url": "/Users/remko/Sites/_plugins/kai-personalize"
        }
    ],
    "require": {
        "keyagency/kai-personalize": "dev-main"
    }
}
```

> **Note:** The path repository tells Composer "this package is right here on my disk." Using just `"dev-main"` without the path repository makes Composer look for the package on Packagist.

2. **Install the addon:**

```
composer update keyagency/kai-personalize
```

3. **Verify the symlink was created:**

```
ls -la vendor/keyagency/kai-personalize
# Should show: kai-personalize -> /Users/remko/Sites/_plugins/kai-personalize
```

4. **Develop normally** - Changes in the addon directory reflect instantly in your project. Commit in the addon repo separately.

This is the standard Statamic addon development workflow recommended by the Statamic team.

### Running Tests

[](#running-tests)

```
composer test
```

### Code Style

[](#code-style)

```
composer format
```

Support
-------

[](#support)

For support, please contact:

- **Email**:
- **Website**:

License
-------

[](#license)

Proprietary - Copyright © Key Agency

Credits
-------

[](#credits)

Developed by Key Agency with AI agent Kai.

Roadmap
-------

[](#roadmap)

### ✅ Completed (v1.0)

[](#-completed-v10)

- Control Panel UI for Rules management
- Control Panel UI for Visitors management
- Control Panel UI for Segments management
- Control Panel UI for API Connections management
- Dashboard with real-time statistics
- Settings management interface

### ✅ Completed (v1.1)

[](#-completed-v11)

- Analytics &amp; Engagement Scoring
- Page-level analytics with scroll depth and reading time
- Visitor page history with pagination
- Behavioral summary (max scroll, reading time, clicks, events)
- Top engaged visitors ranking
- Event tracking tags (kai:track, kai:behavior)

### Short Term (Next Release)

[](#short-term-next-release)

- Enhanced Dashboard with charts and graphs
- Export/Import functionality for rules and settings
- Segment-based condition support in Rules

### Medium Term

[](#medium-term)

- A/B testing capabilities
- Rule templates and presets
- Visitor journey visualization
- Segment-based condition support in Rules

### Long Term

[](#long-term)

- Machine learning predictions
- GraphQL API support
- WebSocket connections for real-time data
- Content recommendation engine
- Multi-variant testing
- More API provider integrations

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a06cd15447fcfb631b0e29aa3c1a294231260879dfbc3f8e27d8b12f5376d65c?d=identicon)[Key Agency](/maintainers/Key%20Agency)

---

Top Contributors

[![remko-key](https://avatars.githubusercontent.com/u/273132?v=4)](https://github.com/remko-key "remko-key (7 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/keyagency-kai-personalize/health.svg)

```
[![Health](https://phpackages.com/badges/keyagency-kai-personalize/health.svg)](https://phpackages.com/packages/keyagency-kai-personalize)
```

###  Alternatives

[microweber/microweber

New generation CMS with drag and drop

3.4k13.8k](/packages/microweber-microweber)[networking/init-cms-bundle

This Bundle is a basic cms based on symfony

1006.0k2](/packages/networking-init-cms-bundle)[deltasystems/dewdrop

121.0k](/packages/deltasystems-dewdrop)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
