---
title: "Write an audit event for every sensitive · Agent Wranglers"
url: https://agentwranglers.se/playbook/compliance/audit-logs-and-access/audit-log-events
updated: 2026-10-20
licence: "All rights reserved"
---

# Write an audit event for every sensitive action

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Audit logs and access](https://agentwranglers.se/playbook/compliance/audit-logs-and-access)
- Rule: Write one audit event for each action on a fixed list, with who, what, which record, when, from where and the result.
- How: The list covers sign-ins, failed sign-ins, two-factor changes, role changes and admin actions. It also covers data exports, deletions, API keys, payout details and security settings. Reading a person's data is covered in the [access logging rule](https://agentwranglers.se/playbook/data/personal-data/access-logging). Money movements have their own [money audit log](https://agentwranglers.se/playbook/payments/invoices-and-records/money-audit-log) and [payment ledger](https://agentwranglers.se/playbook/payments/payment-events-and-reconciliation/payment-ledger). Mark whether the actor is a person, a service or an AI agent. Use UTC times and the request ID. Record "from where" as a shortened IP address or a place, as [keep only what you need](https://agentwranglers.se/playbook/website/privacy/data-minimization) asks. Store IDs, never the data itself. ISO/IEC 27001:2022 control A.8.15 and SOC 2 criterion CC7.2 both expect this log.
- Check: An end-to-end test performs every action on the list. For each, the audit store holds exactly one event with every field filled.

Example:

```js
export function auditEvent({ actor, actorType, action, target, requestId, sourceIp, outcome }) {
  return {
    at: new Date().toISOString(),
    actor, actor_type: actorType,
    action, target,
    request_id: requestId, source_ip: sourceIp,
    outcome,
  };
}
await auditLog.append(auditEvent({ actor: 'u_381', actorType: 'person', action: 'role.grant', target: 'u_902:admin', requestId, sourceIp, outcome: 'success' }));
```

```python
from datetime import datetime, timezone

def audit_event(*, actor, actor_type, action, target, request_id, source_ip, outcome) -> dict:
    return {
        "at": datetime.now(timezone.utc).isoformat(),
        "actor": actor, "actor_type": actor_type,
        "action": action, "target": target,
        "request_id": request_id, "source_ip": source_ip,
        "outcome": outcome,
    }

audit_log.append(audit_event(actor="u_381", actor_type="person", action="role.grant",
                             target="u_902:admin", request_id=request_id, source_ip=source_ip, outcome="success"))
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers compliance playbook: https://agentwranglers.se/playbook/compliance/audit-logs-and-access/audit-log-events`.

- Next: [Make the audit log impossible to change](https://agentwranglers.se/playbook/compliance/audit-logs-and-access/audit-log-immutable)
- [Audit logs and access](https://agentwranglers.se/playbook/compliance/audit-logs-and-access)
