Matomo Event Tracking
Overview
Matomo (formerly Piwik) is an open-source analytics platform that offers a privacy-focused alternative to Google Analytics. As a self-hosted or cloud solution, Matomo gives you complete ownership of your data while providing robust event tracking capabilities similar to commercial platforms.
Matomo's event tracking model is straightforward: events consist of a category, action, name, and optional value. This simple structure makes it easy to implement while still providing the flexibility needed for complex tracking scenarios. Combined with custom dimensions and content tracking, Matomo offers comprehensive measurement for websites and applications.
Event Model
How Matomo Events Work
Every event in Matomo has four components:
- Category: The event category (e.g., "Videos", "Downloads", "Outbound Links")
- Action: The action performed (e.g., "Play", "Pause", "Download")
- Name: Optional - The event name/label (e.g., "Product Demo Video", "Whitepaper.pdf")
- Value: Optional - Numeric value (e.g., video duration, file size)
Event Structure:
{
"category": "Videos",
"action": "Play",
"name": "Product Demo Tutorial",
"value": 180 // Optional numeric value
}
Event Characteristics
- Category and Action are required
- Name and Value are optional
- Events are tied to pageviews in session context
- Can be tracked client-side or server-side
- Support for custom dimensions and variables
Limits
- Category name: 200 characters
- Action name: 200 characters
- Event name: 200 characters
- Value: Positive integer or float
- Events per day: Unlimited (self-hosted), varies by cloud plan
- Custom dimensions: 5 (free), up to 500 (premium)
Standard Events
Matomo provides built-in tracking for common interactions that can be enabled without custom code.
Automatically Tracked
Pageviews:
// Automatic on every page load
// No code needed - just include tracking snippet
Downloads:
// Automatic tracking of file downloads
// Tracks: .pdf, .doc, .xls, .zip, .mp3, .mp4, etc.
// Category: "Downloads"
// Action: "Download"
// Name: File name/URL
Outbound Links:
// Automatic tracking of external links
// Category: "Outbound Links"
// Action: "Click"
// Name: Destination URL
Enable/Disable Automatic Tracking
// Disable automatic tracking
_paq.push(['disableDownloadTracking']);
_paq.push(['disableOutlinkTracking']);
// Enable specific file extensions for download tracking
_paq.push(['addDownloadExtensions', 'dmg|pkg']);
// Set custom link classes
_paq.push(['setLinkClasses', 'custom-download-class']);
Content Tracking
Track content impressions and interactions:
<!-- Track content block -->
<div data-track-content>
<a href="/product" data-content-name="Product Banner" data-content-piece="Homepage Hero">
<img src="banner.jpg" data-content-target="/product" />
</a>
</div>
// Enable content tracking
_paq.push(['trackAllContentImpressions']);
_paq.push(['trackVisibleContentImpressions']);
// Track interaction
_paq.push(['trackContentInteraction', 'click', 'Product Banner', 'Homepage Hero', '/product']);
Custom Events
Custom events let you track specific interactions meaningful to your business.
Basic Event Tracking
// Basic event (category + action)
_paq.push(['trackEvent', 'Videos', 'Play']);
// Event with name
_paq.push(['trackEvent', 'Videos', 'Play', 'Product Demo']);
// Event with value
_paq.push(['trackEvent', 'Videos', 'Play', 'Product Demo', 180]);
// Complete event
_paq.push(['trackEvent',
'Button Clicks', // Category
'Click', // Action
'CTA - Homepage Sign Up', // Name (optional)
10 // Value (optional)
]);
Common Event Examples
Button/Link Clicks:
// CTA button
_paq.push(['trackEvent', 'Engagement', 'Button Click', 'Sign Up CTA']);
// Navigation
_paq.push(['trackEvent', 'Navigation', 'Menu Click', 'Pricing Page']);
// Social sharing
_paq.push(['trackEvent', 'Social', 'Share', 'Twitter']);
Form Tracking:
// Form start
_paq.push(['trackEvent', 'Forms', 'Start', 'Contact Form']);
// Form submission
_paq.push(['trackEvent', 'Forms', 'Submit', 'Contact Form']);
// Form abandonment
_paq.push(['trackEvent', 'Forms', 'Abandon', 'Contact Form', abandonedFieldCount]);
Media Engagement:
// Video events
_paq.push(['trackEvent', 'Video', 'Play', 'Product Demo', 0]);
_paq.push(['trackEvent', 'Video', 'Pause', 'Product Demo', 45]);
_paq.push(['trackEvent', 'Video', 'Complete', 'Product Demo', 180]);
// Audio events
_paq.push(['trackEvent', 'Podcast', 'Play', 'Episode 42']);
_paq.push(['trackEvent', 'Podcast', 'Skip', 'Episode 42', 300]);
File Downloads:
// Custom download tracking
_paq.push(['trackEvent', 'Downloads', 'PDF', 'Product Brochure']);
_paq.push(['trackEvent', 'Downloads', 'Software', 'App v2.1.0', fileSize]);
Event Tracking with Custom Dimensions
// Set custom dimension
_paq.push(['setCustomDimension', 1, 'Premium User']);
// Track event (includes custom dimension)
_paq.push(['trackEvent', 'Feature', 'Use', 'Advanced Reports']);
// Delete custom dimension
_paq.push(['deleteCustomDimension', 1]);
Ecommerce Events
Matomo has comprehensive ecommerce tracking built-in.
Ecommerce Setup
// Enable ecommerce for site
// (Set in Matomo admin: Settings > Websites > Ecommerce)
Product Views
// Track product view
_paq.push(['setEcommerceView',
'SKU_12345', // Product SKU (required)
'Wireless Headphones', // Product name (optional but recommended)
'Electronics', // Category (optional)
199.99 // Price (optional)
]);
_paq.push(['trackPageView']);
Category Pages
// Track category view
_paq.push(['setEcommerceView',
false, // No product SKU
false, // No product name
'Electronics' // Category name
]);
_paq.push(['trackPageView']);
Add to Cart
_paq.push(['addEcommerceItem',
'SKU_12345', // Product SKU (required)
'Wireless Headphones', // Product name (optional)
'Electronics', // Category (optional)
199.99, // Price (optional)
1 // Quantity (optional, default 1)
]);
// Track cart update
_paq.push(['trackEcommerceCartUpdate', 199.99]);
Remove from Cart
_paq.push(['removeEcommerceItem', 'SKU_12345']);
_paq.push(['trackEcommerceCartUpdate', 0]);
Complete Purchase
// Add all purchased items
_paq.push(['addEcommerceItem',
'SKU_12345',
'Wireless Headphones',
['Electronics', 'Audio'],
199.99,
1
]);
_paq.push(['addEcommerceItem',
'SKU_67890',
'Phone Case',
'Accessories',
29.99,
2
]);
// Track order
_paq.push(['trackEcommerceOrder',
'ORDER_12345', // Order ID (required)
259.97, // Grand total (required)
224.97, // Sub total (optional)
20.00, // Tax (optional)
15.00, // Shipping (optional)
25.00 // Discount (optional)
]);
Abandoned Cart
// Track cart without purchase
_paq.push(['addEcommerceItem', 'SKU_12345', 'Product Name', 'Category', 199.99, 1]);
_paq.push(['trackEcommerceCartUpdate', 199.99]);
// User leaves - Matomo identifies as abandoned cart
User Properties
Matomo uses Custom Dimensions and Custom Variables for user-level attributes.
Custom Dimensions (Recommended)
Custom dimensions must be configured in Matomo admin first:
Setup in Admin:
- Go to Administration > Websites > Custom Dimensions
- Click Add Custom Dimension
- Choose scope: Visit or Action
- Name it (e.g., "User Type", "Subscription Plan")
Implementation:
// Set visit-scoped dimension (persists for visit)
_paq.push(['setCustomDimension', dimensionId, 'Premium User']);
// Track pageview (includes dimension)
_paq.push(['trackPageView']);
// Set action-scoped dimension (only for next action)
_paq.push(['setCustomDimension', dimensionId, 'Feature: Reports']);
_paq.push(['trackEvent', 'Feature', 'Use', 'Advanced Reports']);
// Delete dimension
_paq.push(['deleteCustomDimension', dimensionId]);
Example - User Attributes:
// User tier (dimension ID 1)
_paq.push(['setCustomDimension', 1, 'Premium']);
// User role (dimension ID 2)
_paq.push(['setCustomDimension', 2, 'Admin']);
// User segment (dimension ID 3)
_paq.push(['setCustomDimension', 3, 'High Value']);
_paq.push(['trackPageView']);
Custom Variables (Legacy)
Custom variables are older but still supported:
// Set visit-scoped variable
_paq.push(['setCustomVariable',
1, // Index (1-5)
'UserType', // Name
'Premium', // Value
'visit' // Scope: 'visit' or 'page'
]);
// Set page-scoped variable
_paq.push(['setCustomVariable',
2,
'Category',
'Electronics',
'page'
]);
_paq.push(['trackPageView']);
// Get custom variable
var userType = _paq.getCustomVariable(1, 'visit');
// Delete custom variable
_paq.push(['deleteCustomVariable', 1, 'visit']);
User ID Tracking
Track logged-in users across devices:
// Set user ID
_paq.push(['setUserId', 'user_12345']);
_paq.push(['trackPageView']);
// Reset user ID on logout
_paq.push(['resetUserId']);
_paq.push(['trackPageView']);
Event Parameters
While Matomo events don't have "parameters" like GA4, you can add context through custom dimensions and variables.
Adding Context to Events
Using Custom Dimensions:
// Set context via custom dimension
_paq.push(['setCustomDimension', 1, 'Mobile App']);
_paq.push(['setCustomDimension', 2, 'iOS']);
// Track event (includes dimensions)
_paq.push(['trackEvent', 'Video', 'Play', 'Tutorial', 180]);
Using Event Name for Context:
// Encode context in event name
_paq.push(['trackEvent', 'Video', 'Play', 'Tutorial - Getting Started - Homepage']);
// Or use structured naming
_paq.push(['trackEvent', 'Video', 'Play', JSON.stringify({
title: 'Tutorial',
section: 'Getting Started',
location: 'Homepage'
})]);
Built-in Context
Matomo automatically captures:
- Page URL: Current page
- Page Title: Document title
- Referrer: Previous page/site
- User Agent: Browser/device info
- Location: IP-based geolocation
- Resolution: Screen resolution
- Timestamp: Event time
- Session: Session ID and visit count
Implementation Methods
1. JavaScript Tracking Code
Basic Setup:
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//your-matomo-domain.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
With Events:
// Track events
document.getElementById('signup-btn').addEventListener('click', function() {
_paq.push(['trackEvent', 'Engagement', 'Click', 'Sign Up Button']);
});
// Track with custom dimensions
document.getElementById('premium-feature').addEventListener('click', function() {
_paq.push(['setCustomDimension', 1, 'Premium User']);
_paq.push(['trackEvent', 'Features', 'Use', 'Advanced Reports']);
});
2. Tag Manager Integration
Matomo Tag Manager (similar to Google Tag Manager):
Setup:
- Install Matomo Tag Manager container code
- Create triggers (click, pageview, custom event)
- Create tags (Matomo Analytics tags)
- Configure variables
- Publish container
Example Tag:
- Trigger: Click on .cta-button
- Tag Type: Matomo Analytics - Event
- Category:
\{\{Click Class\}\} - Action: Click
- Name:
\{\{Click Text\}\}
3. Server-Side Tracking API
PHP:
<?php
require 'vendor/autoload.php';
use MatomoTracker;
$tracker = new MatomoTracker($siteId = 1, 'https://your-matomo-domain.com/matomo.php');
// Set user
$tracker->setUserId('user_12345');
$tracker->setTokenAuth('YOUR_AUTH_TOKEN');
// Track event
$tracker->doTrackEvent(
'Purchase', // Category
'Complete', // Action
'Order_12345', // Name
499.99 // Value
);
Python:
from matomo_tracker import MatomoTracker
tracker = MatomoTracker('YOUR_SITE_ID', 'https://your-matomo-domain.com/matomo.php')
# Track event
tracker.track_event(
category='Video',
action='Play',
name='Product Demo',
value=180,
uid='user_12345'
)
# Track ecommerce order
tracker.track_ecommerce_order(
order_id='ORDER_12345',
grand_total=499.99,
sub_total=459.99,
tax=40.00,
shipping=0,
discount=50.00
)
curl "https://your-matomo-domain.com/matomo.php?
idsite=1&
rec=1&
action_name=Page%20Title&
url=https://example.com/page&
e_c=Videos&
e_a=Play&
e_n=Product%20Demo&
e_v=180&
_id=USER_ID&
token_auth=YOUR_TOKEN"
4. Mobile SDKs
iOS (Swift):
import MatomoTracker
// Initialize
let tracker = MatomoTracker(
siteId: "1",
baseURL: URL(string: "https://your-matomo-domain.com/matomo.php")!
)
// Track event
let event = Event(
tracker: tracker,
action: ["Videos", "Play"],
name: "Product Demo",
value: 180
)
tracker.track(event)
// Track screen view
let screenView = Event(tracker: tracker, action: ["screen", "Home Screen"])
tracker.track(screenView)
Android (Kotlin):
import org.matomo.sdk.Tracker
import org.matomo.sdk.TrackMe
import org.matomo.sdk.extra.TrackHelper
// Initialize
val tracker = Tracker.Builder(
"https://your-matomo-domain.com/matomo.php",
1,
"DefaultTracker"
).build()
// Track event
TrackHelper.track()
.event("Videos", "Play")
.name("Product Demo")
.value(180f)
.with(tracker)
// Track screen
TrackHelper.track()
.screen("/home")
.title("Home Screen")
.with(tracker)
Debugging & Validation
1. Browser Console Debugging
// Check if Matomo is loaded
console.log(typeof _paq);
// Get tracker instance
var tracker = Matomo.getAsyncTracker();
// Check configuration
console.log(tracker.getSiteId());
console.log(tracker.getTrackerUrl());
// Get visitor ID
tracker.getVisitorId(function(visitorId) {
console.log('Visitor ID:', visitorId);
});
// Get visitor info
tracker.getVisitorInfo(function(info) {
console.log('Visitor Info:', info);
});
2. Network Inspection
DevTools Network Tab:
Filter: matomo.php
Look for: GET/POST requests
Check parameters:
- idsite (site ID)
- rec=1 (record request)
- e_c (event category)
- e_a (event action)
- e_n (event name)
- e_v (event value)
3. Real-Time Widget
In Matomo dashboard:
- Add Visitors > Real-time widget
- Perform actions on site
- See visitors and events in real-time
- Click visitor to see details
4. Visitor Log
Detailed visitor tracking:
- Go to Visitors > Visitor Log
- Find your visit (filter by IP if needed)
- Click to expand session
- See all pageviews and events
- Validate event properties
5. Debug Tracking Request
Add debug parameter to tracking URL:
_paq.push(['setTrackerUrl', u+'matomo.php?debug=1']);
Or visit in browser:
https://your-matomo-domain.com/matomo.php?debug=1&...parameters...
6. Browser Extension
Matomo Tag Manager Debug Mode:
- Enable debug mode in container
- See triggers, variables, tags firing
- Validate event data before send
Best Practices
Event Organization
✅ Do:
- Use consistent category names: "Videos", "Forms", "Downloads"
- Use descriptive actions: "Play", "Submit", "Click"
- Include meaningful names: "Product Demo Video", "Contact Form"
- Use values appropriately: duration, file size, price
- Document your event taxonomy
❌ Don't:
- Mix granularity levels in categories
- Use generic names: "Event1", "Action"
- Include dynamic values in categories/actions
- Exceed character limits (200 chars)
Event Hierarchy
Good structure:
Category: Videos
Action: Play
Action: Pause
Action: Complete
Name: [Video Title]
Value: [Duration in seconds]
Category: Forms
Action: Start
Action: Submit
Action: Abandon
Name: [Form Name]
Value: [Time spent]
Category: Ecommerce
Action: Add to Cart
Action: Remove from Cart
Action: Purchase
Name: [Product Name]
Value: [Price]
Custom Dimensions
✅ Do:
- Configure dimensions in admin before use
- Use visit-scope for user attributes
- Use action-scope for event-specific data
- Name dimensions clearly
- Document dimension IDs and purposes
❌ Don't:
- Exceed dimension limits
- Mix scopes inappropriately
- Use dimensions for sensitive PII
- Change dimension meanings over time
Ecommerce Tracking
✅ Do:
- Use unique order IDs
- Track all cart interactions
- Include product categories
- Set accurate prices and quantities
- Track both adds and removes
❌ Don't:
- Submit duplicate orders
- Miss cart abandonment tracking
- Forget to clear cart after purchase
- Track test orders in production
Privacy & Compliance
✅ Do:
// Honor Do Not Track
_paq.push(['setDoNotTrack', true]);
// Anonymize IP addresses
_paq.push(['setIpAnonymization', true]);
// Disable cookies
_paq.push(['disableCookies']);
// Require consent
_paq.push(['requireConsent']);
_paq.push(['setConsentGiven']); // After user accepts
// Cookie consent
_paq.push(['requireCookieConsent']);
_paq.push(['setCookieConsentGiven']); // After user accepts
❌ Don't:
- Track without user consent (GDPR)
- Store PII without hashing
- Ignore Do Not Track headers
- Use default settings without review
Performance
- Load Matomo asynchronously
- Use
_paq.push()for all tracking calls - Batch events when possible
- Consider self-hosting for faster loads
- Use CDN for Matomo.js (if cloud-hosted)
Data Quality
- Filter internal traffic by IP
- Enable bot detection
- Regularly review event reports
- Validate ecommerce data accuracy
- Monitor for tracking errors
Filter Internal Traffic:
Settings > Websites > [Your Site] > Excluded IPs
Add: 192.168.1.0/24
Additional Resources: