Skip to content

Activity Log Since 3.6.3

Utilities → Activity Log shows who did what on the site, newest first. Every row names the person, API key or AI client behind it, what it touched, and when. It is on in every edition and needs no setup.

It answers questions like:

  • Which member downloaded the price list last week?
  • What did the AI client a member connected actually do?
  • Who deleted that collection?
  • Why does one account keep getting refused?
EventRecorded when
object.created, object.updated, object.deletedAn object is saved or deleted. Updates list the names of the fields that changed, never their values.
collection.created, collection.updated, collection.deletedA collection is created, its settings change, or it is deleted. Index rebuilds after an object save are left out.
schema.saved, schema.deleted, template.savedA schema or Site Builder template is saved or deleted.
user.login, user.logoutSomeone signs in or out.
file.downloadedA signed-in user downloads a file from a /download/... link. Downloads by visitors who are not signed in are not recorded.
access.deniedA request is refused by access groups, ownership or a protected field.
mcp.tool.calledAn AI client calls an MCP tool: the tool, its target (collection, ID, query) and how long it took. The content an agent writes is never stored.
import.completed, bulk.deletedAn import finishes (one row with the counts, not one per object), or objects are deleted in bulk.
extension.*, devmode.*, cache.clearedAn extension is enabled or disabled, dev mode is toggled, or the cache is cleared.

Deletes, bulk deletes, refusals and failed MCP calls are marked as warning, so they stand out and can be filtered on their own.

Each row records the actor the event payload carries:

  • Who: the user and their auth collection, or the API key, or system for CLI commands and background jobs.
  • via: the OAuth client a member connected through, such as an AI assistant. Filter on the client ID to see everything that client did.
  • for: when the system did the work on someone’s behalf (an automation an admin ran, a login stamp), the person who started it.

Filter by event, kind of actor, collection, level and date range. The User, key or client box matches a user ID, an API key ID or an OAuth client ID, so one box finds a member, a key or an AI client. Clicking a user, event or collection in the table adds it to the filters.

Access is the same as the other utilities: admins see it, and an access group can grant it under Specific Utilities → Activity Log.

Export CSV downloads every row the current filters match, not just the page on screen, with the date, event, level, actor, user, auth collection, OAuth client, API key, who it was on behalf of, collection, object, summary and detail. The file is named after the date range, for example activity-2026-10-01-to-2026-10-22.csv. Cells a spreadsheet would read as a formula are escaped, since object IDs and file names can come from visitors.

Settings → Activity:

SettingDefault
Record ActivityOnMaster switch. Turning it off keeps existing rows.
Keep Activity (days)90Older rows are deleted, checked once a day.
Record Anonymous MCP CallsOffTool calls from AI clients that are not signed in. A public MCP server listed in an AI directory can take thousands of anonymous lookups a day, so these are left out unless you ask for them. Signed-in callers and API keys are always recorded.

The same keys can be set in config/tcms.php:

return [
'activity' => [
'enable' => true,
'retentionDays' => 365,
'mcpAnonymous' => false,
],
];

Rows live in a SQLite database at tcms-data/.system/activity, which survives application updates. A row is a few hundred bytes. Recording never blocks the action being recorded: if the database cannot be written, the failure is logged to totalcms.log (channel activity) and the action carries on.

file.downloaded is a core event, so it can also trigger an automation or an extension listener, for example to email someone when a member downloads a contract.