Audit trails
GCM keeps three separate audit trails so you can answer "who changed this?" without trawling through database backups. Each one lives in a different place because each one is asking a different question.

The three logs
| Log | Lives at | Answers |
|---|---|---|
| Organization audit log | /activity-log and Data & Logs → Audit Log | Who in your org created, edited, or deleted a member, donation, group, ministry, school, attendance, or org unit? |
| Member activity log | A tab on every member's profile | What's happened to this person — joined a group, attended a meeting, received a workflow message, marked lost, recovered? |
| Import history | Data & Logs → Import History | Did the CSV import I ran last week finish, and how many rows failed? |
There's also a fourth log — the platform audit log — but it's reserved for Genius Church Manager platform admins (us) and tracks cross-tenant impersonation, plan changes, and superadmin role moves. You won't see it from inside your org.
Who can see audit data
| Permission | Can see |
|---|---|
users.manage | The full /activity-log page across all entity types and actors |
config.manage | The Audit Log tab inside Data & Logs (same data, abbreviated view) |
| Any member's profile viewer | That member's own activity timeline |
| None of the above | Nothing — audit entries don't leak through other screens |
The activity log is permission-gated to users.manage on purpose: most staff don't need to see every teammate's edits, and exposing that data by default invites uncomfortable HR conversations.
What gets logged automatically
A generic audit_log_writes() Postgres trigger watches eight entity tables and writes one row to org_audit_log per material change:
membersattendancesdonationsgroupsministriesorg_unitsschoolsschool_enrolments
Material change means: an INSERT, an UPDATE that touches at least one non-system column, or a soft delete (deleted_at set). Bulk updates from imports, workflow actions, and the API all flow through the same trigger — there is no way to write to those tables and bypass the log.
The trigger captures:
- Actor — the authenticated user's UUID and email (or
systemfor cron/webhook writes) - Action —
created,updated,deleted,restored, plus domain-specific verbs likemerge_members - Entity — type + ID, so the activity-log row can link directly to the affected member or group
- Metadata — for updates, the list of changed columns (useful for "what did you change?" but not the before/after values)
Member-specific events (joined a group, marked lost, received a workflow message) are written to a second table, member_activity_log, by application code rather than a trigger — that's the timeline you see on the member profile.
What is not logged
Two deliberate omissions:
- Read access. Looking at a member doesn't write a row. If you need view-tracking for compliance, talk to us about the enterprise add-on.
- Before/after column values. Storing them would multiply log size by 10-50x. The "changed" array tells you which columns moved, not what they moved from and to. If you need point-in-time member state, use database backups instead.
Retention
Audit rows are kept indefinitely on paid plans and pruned to the last 90 days on Starter. You can export the log before it ages out if you need long-term records — and you should, for any year you sent tax statements.
Where to go next
- Organization audit log — the full cross-org "who did what" feed
- Member activity log — the per-member timeline
- Import history — bulk-import job status and row failures
- Exporting logs — CSV exports for compliance and backups
TIP
If you're investigating a specific change (a donation amount that looks wrong, a missing member), start at the organization audit log and filter by the entity. It's faster than scrolling the member's timeline.
