Skip to main content

Release annotations

An annotation is a note on your Matomo reports timeline. The most useful thing to put there is a deployment marker, because it turns "traffic dropped on the 14th" into "traffic dropped when we shipped 1.4.0".

Matomo's Annotations plugin is free core. It needs a token_auth belonging to a non-anonymous user, and every call here goes through the same resilience layer as tracking — so a failed annotation never breaks a deploy.

From code​

use MatomoAnalytics\Facades\MatomoAnnotations;

MatomoAnnotations::add('Migrated to PostgreSQL 16', date: '2026-07-03', starred: true);
MatomoAnnotations::annotateRelease('1.4.0'); // "Deployed 1.4.0"
MatomoAnnotations::annotateRelease('1.4.0', site: 2);

add() takes the note, an optional date (YYYY-MM-DD, defaulting to today), whether to star it, and an optional site id. It returns the created annotation, or null on failure with the reason in lastError() — the same convention as reporting.

"Today" is the day in your app.timezone, the one today() returns. Matomo keeps the date as it arrives and draws the marker on that day of the site's timeline. If your Matomo site keeps another time zone, pass the date on its calendar; from a deploy script, for example, --date="$(TZ=Europe/Berlin date +%F)".

Starring matters more than it sounds: Matomo shows starred annotations more prominently on the timeline, so starring releases and leaving routine notes unstarred keeps the timeline readable.

From a deploy pipeline​

php artisan matomo:annotate "Maintenance window"
php artisan matomo:annotate --release # "<prefix> <version>"
'annotations' => [
'release' => env('MATOMO_ANNOTATE_RELEASES', false),
'starred' => false,
'release_prefix' => 'Deployed',
],

matomo:annotate --release is a no-op unless you opt in with annotations.release (MATOMO_ANNOTATE_RELEASES=true). It prints that it is disabled and exits successfully, so you can drop it unconditionally into every deploy — including staging, where you probably do not want markers — and enable it only where it should fire.

It also needs a token_auth (MATOMO_TOKEN) for a non-anonymous user, so it stays inert in an environment that has none.

Where the version comes from​

This is the one part with a real trap in it.

The version comes from --app-version. Without that flag the command falls back to config('app.version') — which stock Laravel does not define. So either pass it explicitly:

php artisan matomo:annotate --release --app-version=1.4.0 # posts "Deployed 1.4.0"

…or expose an app version once, and plain --release picks it up:

// config/app.php
'version' => env('APP_VERSION'),
APP_VERSION=1.4.0

If no version resolves at all, the note is just the prefix — Deployed — which is a usable marker but tells you nothing about what was deployed. That silent degradation is why this is worth getting right once.

The flag is --app-version, not --version. Symfony reserves --version on every Artisan command, so it could not be used here.

The other options​

php artisan matomo:annotate "Note" --date=2026-07-01 --starred --site=2

--date sets the annotation date, --starred stars this one note regardless of the config default, and --site targets a site other than the configured one.

In tests​

MatomoAnnotations::fake() records annotations and can be made to fail, so a deploy hook can be tested without a token. See testing.