May 1, 2026by Auditledge

What Is an Audit Log (And Why Every SaaS Needs One)

An audit log records who did what and when inside your application. Here's what that means in practice, what to capture, and why customers and compliance teams will ask for it.

The one-line definition

An audit log is an immutable, chronological record of every meaningful action taken inside your application — who performed it, what they changed, and when it happened.

Think of it as your app's security camera footage. You hope you never need it, but when something goes wrong — a record deleted, a setting changed, a suspicious login — it's the first thing everyone asks for.

What belongs in an audit log

Not every database write deserves an audit event. Focus on actions that matter to accountability:

  • Authentication events — logins, logouts, failed attempts, password changes, MFA enrollment
  • User management — invitations, role changes, deactivations
  • Data mutations — creating, updating, or deleting records that carry business value
  • Permission changes — who gained or lost access to what
  • Configuration changes — billing plan updates, integration settings, API key creation or revocation
  • Exports and data access — any time a user reads sensitive data in bulk

A good rule of thumb: if a customer's compliance officer or your own security team would want to know about it in a post-incident review, log it.

The anatomy of an audit event

Every event should capture at minimum:

{
  "actor": {
    "id": "user_abc123",
    "name": "Alice Johnson",
    "email": "alice@acme.com"
  },
  "action": "invoice.deleted",
  "resource": {
    "type": "invoice",
    "id": "inv_789",
    "name": "Invoice #1042"
  },
  "organization_id": "org_acme",
  "timestamp": "2026-05-01T14:32:00Z",
  "metadata": {
    "ip": "203.0.113.42",
    "user_agent": "Mozilla/5.0..."
  }
}

Actor — the human or service that performed the action. Always store the ID, and store a snapshot of the name and email at the time of the event. Users get renamed; IDs don't change.

Action — use a consistent dot-notation verb: resource.action. Past tense works too (invoice.deleted vs invoice.delete). Pick one and stick to it.

Resource — what was acted upon. Storing both the ID and a human-readable name saves your support team hours.

Timestamp — always UTC, always ISO 8601. Timezone ambiguity in audit logs is a nightmare.

Metadata — IP address and user agent are almost always worth capturing. Add anything else relevant to your domain.

Why customers will ask for this

Enterprise sales

The moment you start selling to companies with more than ~50 employees, procurement checklists appear. "Does your product have an audit log?" is on every one of them. The answer needs to be yes, and ideally there's a UI where their admins can see it.

Compliance frameworks

SOC 2 Type II — logging user activity and access is a control requirement under the CC6 and CC7 control categories. Auditors will ask to see evidence.

GDPR / DPDPA / PIPEDA — demonstrating who accessed personal data and when is required to respond to subject access requests and demonstrate accountability to regulators.

HIPAA — the Access Controls and Audit Controls standards (§164.312) require covered entities to record and examine activity in systems containing electronic protected health information.

PCI DSS — Requirement 10 mandates tracking all access to cardholder data.

Internal accountability

Beyond compliance, audit logs change team behaviour. When people know their actions are recorded, they think twice. When something breaks, an audit log turns a multi-hour investigation into a five-minute search.

What makes a bad audit log

An audit log is only useful if you can trust it. Common mistakes that undermine that trust:

  • Mutable records — if rows can be updated or deleted, it's not an audit log, it's just a table. Events must be append-only.
  • Missing the actor — "the record was deleted" is useless. "Alice deleted the record from IP 203.0.113.42 at 14:32 UTC" is actionable.
  • Inconsistent action namesuser_deleted, userDeleted, delete_user, and user.delete all meaning the same thing makes querying a nightmare.
  • No retention policy — storing everything forever gets expensive. Storing nothing after 7 days gets you into compliance trouble. Define your retention window by plan and document it.
  • Burying it in the database — if your audit log is just a table in your main Postgres instance, a compromised admin account can tamper with it. Consider writing to a separate, append-only store.

Getting started in 10 minutes

If you're building from scratch, the minimum viable audit log is:

  1. A separate table (or service) that only supports inserts — no updates, no deletes
  2. A helper function you call from your application code after each meaningful action
  3. A simple query interface so admins can filter by user, action, and date range

If you'd rather not build and maintain this yourself, Auditledge gives you an API endpoint to send events and a dashboard your customers can use — without any infrastructure to manage.

from auditledge import AuditLedge

audit = AuditLedge('your_api_key')

def delete_invoice(user, invoice):
    invoice.delete()
    audit.log({
        'actor': {'id': user.id, 'name': user.name, 'email': user.email},
        'action': 'invoice.deleted',
        'resource': {'type': 'invoice', 'id': invoice.id, 'name': invoice.number},
        'organization_id': user.organization_id,
    })

That's it. One API call per action, and you have a queryable, tamper-evident audit trail your customers and compliance team can rely on.

Add audit logs to your app in minutes

One API call per action. Queryable dashboard. No infrastructure to manage.

Start free →