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"
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.
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--versionon 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.