Audit Logs for SaaS: What to Track and How to Build Them

Nov 25, 2025·13 min read

Audit Logs for SaaS: What to Track and How to Build Them

Summarize this article

Audit logs are one of those features that nobody thinks about until they need them urgently — and then they don't exist. An enterprise customer asks for a log of every admin action taken on their account in the past 90 days. A SOC 2 auditor asks for evidence of access controls and change tracking. A customer escalates because something changed in their configuration and no one can explain what. In every one of these scenarios, the absence of audit logging is painful, slow to remediate, and expensive.

The good news is that audit logging isn't architecturally complex. The challenge is building it comprehensively from the start — with a data structure that supports querying, a storage approach that scales, and an access layer that makes logs useful to the people who need them rather than only to engineers with database access.

Two Categories of Events That Matter

There are two distinct audit event categories that every SaaS company should track, and they serve different audiences with different requirements.

The first is customer-facing events: actions taken within the product that affect account data, user access, or shared configuration. These are the events your enterprise customers will ask for in their security review — who invited a new team member, who changed the billing plan, who deleted a record, who modified a shared workflow, who exported data. This audit log lives in the product, visible to account administrators.

The second is internal team events: actions taken by your own ops, support, and engineering teams through your internal tools and admin panel. Who accessed a customer account, who applied a credit, who changed a subscription state, who used impersonation to view a user's session, who ran a bulk data operation. This audit log lives in your admin panel, visible to your compliance and operations teams.

Both categories should be tracked from the beginning. They have different data requirements, different retention policies, and different access controls — but the underlying event capture mechanism is identical. Teams that build only one or the other end up with half the picture: customer-facing logs that don't capture what your own team is doing, or internal logs that can't satisfy an enterprise security review.

The most common implementation mistake is building these as two separate systems with different data models. Building them on the same event store — with an audience field distinguishing customer-visible events from internal-only events — gives you one queryable surface and one maintenance burden.

The Data Structure That Makes Logs Queryable

An audit log entry that's useful is structured consistently and captures every dimension needed to reconstruct what happened and why.

Who: the actor who performed the action, with enough context to identify them — user ID, email address, display name, and whether they were a customer user or an internal team member. If the action was performed by an automated system or background job, that should be recorded explicitly rather than attributed to the last human who triggered it.

What: a structured event type, not a free-text description. The distinction matters enormously at query time. A log entry like "admin@company.com changed subscription for account 12345" is readable but unsearchable at scale. A structured event type like subscription.plan_changed with a separate details field for human-readable context gives you both — queryable event types for filtering and aggregation, readable descriptions for display.

What changed: the before and after state of the affected resource. For a subscription change, this means the previous plan ID and new plan ID, not just "plan was changed." For a permission change, the previous role and new role. Storing diffs in a structured format ({"from": "pro", "to": "enterprise"}) makes it possible to filter by what specifically changed, not just that a change occurred.

On what: the affected entity type and ID — account, user, subscription, API key, webhook, etc. This makes it possible to pull all events affecting a specific resource: show me everything that happened to account ID 4512 in the last 30 days.

When: a UTC timestamp with millisecond precision. Timezone-naive timestamps create ambiguity that's painful to untangle when investigating incidents that span time zones or daylight saving time boundaries.

From where: the source of the action — the product UI, the internal admin panel, the public API, a background automation. Source attribution tells you whether an action was user-initiated or system-initiated, which matters when a customer disputes a change.

Storage Architecture and Retention

Audit log data has specific characteristics that affect storage decisions: it's append-only (you never update or delete log entries), it grows indefinitely, it needs to be queryable, and it has a defined retention period.

At early scale, a dedicated audit_events table in your primary operational database works fine. The table should have an index on (account_id, created_at) for customer-facing queries and (event_type, created_at) for operational queries. Append-only write patterns are efficient in most databases, and the total event volume for a product with a few hundred accounts is manageable.

At larger scale, or if your primary database is under significant read load, you'll want to move audit events to a separate store. Options include:

A dedicated PostgreSQL instance with the same schema, accessed through a read replica for queries. Simple operationally, familiar to most teams, and supports complex queries. The right choice for most SaaS companies up to a few million events per day.

An event streaming architecture (Kafka, Kinesis) with a long-term sink (S3, Snowflake, BigQuery). Better for high write volume and long-term analytical queries, but adds operational complexity. Appropriate when audit events are part of a broader event streaming strategy rather than a standalone feature.

For retention: 12 months is the minimum for most SaaS companies. 24–36 months is appropriate if you're pursuing SOC 2 Type II or serving regulated industries like healthcare or finance. The retention policy should be documented, enforced programmatically (not manually), and visible to the compliance team.

One critical implementation detail: audit events should never be deletable through the application layer. The only way to remove audit data should be through a scheduled retention policy that runs automatically at the defined interval. Application-layer deletion means that a compromised admin account can cover its tracks, which defeats the purpose of the audit log.

Building the Query Surface for Your Ops Team

Event capture is the easy half. The query surface is where most implementations fall short — not because it's technically hard, but because it gets deprioritized. Engineers implement the event capture, note that "logs are in the database," and move on. Then six months later, every audit query goes to engineering because there's no non-engineer-accessible interface.

The access layer should support the queries your ops team runs regularly, without database access:

Account activity timeline: all events for a specific account in a date range, sorted by time, with human-readable descriptions. This is the query that answers customer escalations: "what changed in my account on Tuesday?"

Actor history: all actions performed by a specific user or internal team member in a date range. This answers "what did support rep Sarah do to account 4512 last week?" and is the query your compliance team runs when investigating suspected unauthorized activity.

Event type filter: all events of a specific type across all accounts in a date range. This answers "how many subscription plan changes happened in March?" and "which accounts had billing credits applied in the last 30 days?"

Affected entity search: all events affecting a specific resource — a particular API key, a specific webhook, a user record. This answers "what happened to this API key between when it was created and when it was last used?"

A filter interface with selectors for account, actor, event type, date range, and source covers 90% of operational queries. The remaining 10% — complex multi-dimensional queries — can still go to engineering; the goal is making the common case self-service, not eliminating engineering involvement entirely.

For enterprise customers, the customer-facing audit log should be filterable by the same dimensions (event type, actor, date range) and exportable to CSV. Enterprise security teams routinely export audit data for inclusion in their own internal reporting, and providing that export capability removes a regular support request.

SOC 2 Requirements and the Compliance Case

SOC 2 compliance has a direct dependency on audit logging. The Trust Services Criteria require evidence across three areas where audit logs are the primary or supporting control:

Logical access: who has access to what, and is that access appropriate? Audit logs of user creation, permission changes, and role assignments provide evidence that access is being tracked and managed. Combined with access review procedures, they satisfy CC6.1 and CC6.2.

Change management: what changed in the system, when, and who authorized it? Audit logs of configuration changes, data modifications, and schema changes provide evidence that changes are tracked and attributable. This satisfies CC8.1.

Monitoring: can the company detect unusual activity? Audit logs combined with alerting rules — alerts when an unusual volume of admin actions are taken, when impersonation is used, when bulk data exports occur — provide evidence of monitoring. This satisfies CC7.2 and CC7.3.

Companies that add audit logging retroactively for a SOC 2 audit spend significantly more time and money than those that built it in from the start. Retroactive implementation requires back-filling events (where possible), documenting gaps, and explaining to the auditor why logging only covers a subset of the relevant period. Building it proactively means your first SOC 2 audit reviews a complete, structured audit record — which auditors notice, and which shortens the process considerably.

Practical Implementation Sequence

For teams starting from scratch, the order of implementation matters. Not everything needs to be built at once, but the foundation needs to be right.

Start with the event capture schema and the internal events for your admin panel. Internal team actions are the higher-risk category from a compliance perspective — a compromised support account can do significantly more damage than a compromised customer account — and instrumenting your internal tools is typically faster than instrumenting the full product. Get this working and queryable before moving to customer-facing events.

Second, instrument the highest-stakes customer-facing events: user invitations and removals, permission changes, billing plan changes, and data exports. These are the events enterprise customers will ask for first and that matter most for compliance. Get this working and surfaced in the product for account admins.

Third, expand coverage incrementally: configuration changes, API key operations, webhook events, and any other actions relevant to your specific product. The event capture infrastructure is already in place at this point; it's a matter of instrumenting additional event types against the same schema.

Finally, build the export capability and the alert layer. CSV export for enterprise customers and alerting rules for unusual activity (unusual volume of admin actions, impersonation used outside business hours, bulk data exports) complete the compliance-grade implementation.

A well-scoped audit logging system — capture, storage, query surface, and export — typically takes 4–6 weeks to build for a product of moderate complexity. The compliance value is permanent. Every SOC 2 renewal, every enterprise security review, and every customer escalation involving data changes will draw on the system you built.

Summarize this article

Need audit logging built into your internal tools?

We build internal admin panels and backoffice systems with proper audit logging built in — covering both customer-facing and internal team activity.