How Audit Logging Tracks API Operations in OpenWA: Complete Implementation Guide

OpenWA implements a comprehensive audit logging subsystem using NestJS and TypeORM to capture every significant API interaction, storing detailed context including API keys, session IDs, HTTP metadata, and error states in a searchable database table.

The audit logging system in OpenWA (WhatsApp API) provides administrators with complete visibility into API usage patterns, security events, and operational troubleshooting data. Built on a three-layer architecture, this subsystem automatically records who performed what action, when, and with what result across all WhatsApp session operations.

Architecture Overview

The audit logging implementation follows a standard NestJS modular pattern with clear separation of concerns across three distinct layers:

  1. Controller Layer – Exposes the GET /audit REST endpoint for log retrieval
  2. Service Layer – Contains AuditService with methods for creating, querying, and cleaning up log entries
  3. Persistence Layer – Defines the AuditLog entity schema mapped to the audit_log database table

All components are registered in src/modules/audit/audit.module.ts, which wires together the TypeORM repository, service logic, and HTTP controller.

Core Implementation Files

AuditService: The Central Logging Engine

Located in src/modules/audit/audit.service.ts, the AuditService class provides the primary interface for recording API operations. The core log method accepts an action type, severity level, and a rich context object, then persists an AuditLog entity via TypeORM.

// Core method signature from audit.service.ts
async log(
  action: AuditAction,
  severity: AuditSeverity,
  context: AuditContext
): Promise<AuditLog>

The service exposes convenience wrappers that prepend severity levels:

  • logInfo – Records informational events (successful operations)
  • logWarn – Captures warning states requiring attention
  • logError – Stores failed operations with error details

For retrieval, the findAll method builds dynamic SQL where clauses from AuditQueryOptions, supporting date range filtering, pagination via limit/offset, and severity-based queries. The service also provides cleanup, which removes entries older than a configurable retention period to prevent database bloat.

AuditController: REST API for Log Access

The AuditController in src/modules/audit/audit.controller.ts exposes the GET /audit endpoint for administrators. It translates incoming query parameters into an AuditQueryOptions object and returns paginated results with total counts.

// Example request handling in audit.controller.ts
@Get()
async findAll(@Query() query: AuditQueryOptions) {
  return this.auditService.findAll(query);
}

Query parameters support filtering by action, severity, apiKeyId, sessionId, and date ranges, enabling precise forensic analysis of specific API operations or credential usage.

AuditLog Entity: Database Schema Definition

The AuditLog entity defined in src/modules/audit/entities/audit-log.entity.ts maps to the audit_log table with the following critical columns:

  • action – Enum tracking specific operations (e.g., SEND_MESSAGE, CREATE_GROUP)
  • severity – Classification as INFO, WARN, or ERROR
  • apiKeyId / apiKeyName – Identifies the credential performing the request
  • sessionId / sessionName – Links the log to specific WhatsApp sessions
  • method, path, statusCode – Complete HTTP request context
  • metadata – JSON column for flexible additional data (message IDs, group names)
  • errorMessage – Populated exclusively for failed operations
  • createdAt – Automatic timestamp generated by TypeORM

Logging API Operations in Practice

When processing requests, OpenWA services inject AuditService and invoke logging methods with request context. The following pattern from the source demonstrates tracking a message send operation:

@Injectable()
export class MessageService {
  constructor(
    private readonly audit: AuditService,
    // additional dependencies...
  ) {}

  async sendMessage(payload: SendMessageDto, ctx: RequestContext) {
    try {
      // Business logic to send WhatsApp message...
      
      await this.audit.logInfo(AuditAction.SEND_MESSAGE, {
        apiKey: ctx.apiKey,
        sessionId: ctx.session.id,
        ipAddress: ctx.ip,
        method: ctx.method,
        path: ctx.path,
        statusCode: 200,
        metadata: { messageId: result.id },
      });
    } catch (err) {
      await this.audit.logError(AuditAction.SEND_MESSAGE, {
        apiKey: ctx.apiKey,
        sessionId: ctx.session.id,
        ipAddress: ctx.ip,
        method: ctx.method,
        path: ctx.path,
        statusCode: 500,
        errorMessage: err.message,
      });
      throw err;
    }
  }
}

This implementation ensures both successful operations and failures are captured with identical context, enabling complete audit trails for compliance and debugging.

Querying and Managing Audit Logs

Administrators retrieve logs via standardized HTTP requests:

GET /audit?action=SEND_MESSAGE&severity=INFO&limit=20&offset=0

The response returns a paginated JSON structure:

{
  "data": [
    {
      "id": "c1a2b3d4-e5f6-7890-abcd-1234567890ef",
      "action": "SEND_MESSAGE",
      "severity": "INFO",
      "apiKeyId": "key-123",
      "apiKeyName": "Production API Key",
      "sessionId": "session-abc",
      "ipAddress": "203.0.113.42",
      "method": "POST",
      "path": "/messages",
      "statusCode": 200,
      "metadata": { "messageId": "msg-987" },
      "createdAt": "2026-05-21T14:33:12.000Z"
    }
  ],
  "total": 42
}

For maintenance, the cleanup method in AuditService executes scheduled deletions based on configurable retention days, ensuring the audit_log table remains performant as volume scales.

Summary

  • Three-layer architecture: Controller (GET /audit), Service (AuditService), and Entity (AuditLog) work together in src/modules/audit/
  • Comprehensive context capture: Every log stores API key, session, IP, HTTP method/path, status code, and optional metadata
  • Severity-based logging: Use logInfo, logWarn, or logError to classify events automatically
  • Queryable storage: Filter by action type, date ranges, or credentials via AuditQueryOptions
  • Automatic maintenance: Built-in cleanup functionality prevents unlimited database growth

Frequently Asked Questions

What information does OpenWA store in each audit log entry?

Each entry in the audit_log table captures the action performed (enum), severity level (INFO/WARN/ERROR), the API key and session identifiers, source IP address, HTTP method and path, response statusCode, optional JSON metadata, and errorMessage for failures. The createdAt timestamp is automatically generated by TypeORM when the record persists.

How do I query audit logs via the API?

Send a GET request to the /audit endpoint with query parameters matching AuditQueryOptions. Supported filters include action, severity, apiKeyId, sessionId, and date ranges. The controller returns a paginated response containing both the dataset array and total count, enabling frontend pagination implementations.

Can I configure automatic cleanup of old audit logs?

Yes. The AuditService includes a cleanup method that removes entries older than a configurable number of days. This housekeeping function prevents the audit_log table from growing indefinitely and can be invoked via scheduled tasks or administrative triggers according to your retention policy requirements.

Which API operations trigger audit logging in OpenWA?

Any service can trigger audit logging by injecting AuditService and calling log, logInfo, logWarn, or logError. Common tracked operations include SEND_MESSAGE, session creation, group management, and webhook processing. The system uses the AuditAction enum to standardize operation names across the codebase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →