What Is Included in the Audit Logging for Twenty CRM? A Complete Technical Breakdown

Twenty CRM captures a comprehensive audit trail via the Audit Service, storing workspace events, object record changes, and pageviews in ClickHouse with standardized timestamps, user context, and Zod-validated payloads.

Twenty CRM provides enterprise-grade observability through its audit logging system implemented in the twentyhq/twenty repository. The platform records every meaningful workspace action—from object record mutations to user navigation—within a structured pipeline that ensures data integrity via schema validation before persisting to ClickHouse.

Core Structure of Audit Log Entries

Every audit record in Twenty CRM follows a standardized schema defined in analytics.utils.ts. The common() function (lines 17‑20) generates the base payload that accompanies every event.

Common Fields Every Audit Record Contains

Each log entry includes these immutable fields:

  • timestamp – Formatted as yyyy-MM-dd HH:mm:ss via the common() helper in packages/twenty-server/src/engine/core-modules/audit/utils/analytics.utils.ts
  • version – Schema version identifier (currently '1')
  • workspaceId – The workspace identifier supplied via createContext
  • userId – The user identifier who triggered the action
  • type – Either 'track' for action events or 'page' for pageviews
  • event – Human-readable action name (e.g., Object Record Created)
  • properties – Event-specific details validated against Zod schemas

Event-Specific Payloads and Validation

The properties field varies by event type and is enforced via Zod schemas registered in the events registry. For example, OBJECT_RECORD_CREATED_EVENT in object-record-created.ts defines a loose object schema that captures recordId, objectMetadataId, and custom fields. This validation occurs inside makeTrackEvent before ClickHouse insertion.

How the Audit Service Writes Logs

The AuditService class in packages/twenty-server/src/engine/core-modules/audit/services/audit.service.ts orchestrates all audit writes by establishing a scoped context that binds workspaceId and userId to every subsequent operation.

Creating an Audit Context

Developers initialize an audit context using createContext:

const audit = this.auditService.createContext({
  workspaceId: batch.workspaceId,
  userId: eventData.userId,
});

This context object exposes three specialized writers that handle different audit categories.

ClickHouse Writers and Table Destinations

The context provides three methods that map to distinct ClickHouse tables:

Writer Method ClickHouse Table Implementation Lines
insertWorkspaceEvent workspaceEvent audit.service.ts lines 42‑46
createObjectEvent objectEvent audit.service.ts lines 47‑71
createPageviewEvent pageview audit.service.ts lines 73‑80

Each writer validates the payload against its registered Zod schema via makeTrackEvent or makePageview, then calls clickHouseService.insert to persist the data.

Event Taxonomy and Supported Actions

All trackable events are registered in eventsRegistry using the registerEvent function from track.ts. The system categorizes actions into logical groups for granular observability.

Object Record CRUD Events

The platform audits every mutation on workspace objects through dedicated event constants:

Each definition includes a Zod schema ensuring consistent payload shapes across the audit trail.

Workspace and User Lifecycle Events

Beyond data mutations, Twenty CRM logs administrative and security events:

  • WORKSPACE_ENTITY_CREATED_EVENT – Workspace provisioning
  • USER_SIGNUP_EVENT – User registration events
  • CUSTOM_DOMAIN_ACTIVATED_EVENT / CUSTOM_DOMAIN_DEACTIVATED_EVENT – Domain configuration changes
  • LOGIC_FUNCTION_EXECUTED_EVENT – Serverless function invocations
  • WEBHOOK_RESPONSE_EVENT – External integration callbacks
  • MONITORING_EVENT – System health telemetry

End-to-End Audit Flow

The audit pipeline operates asynchronously through the message queue to ensure zero impact on request latency:

  1. Domain Event Emission – Workspace operations emit events via the workspace-event emitter
  2. Queue Processing – The CreateAuditLogFromInternalEvent processor (packages/twenty-server/src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event.ts, lines 13‑62) receives batched events
  3. Context Initialization – The processor creates an audit context binding workspace and user identifiers
  4. Schema Validation – makeTrackEvent validates payloads against Zod schemas from the events registry
  5. ClickHouse Persistence – Validated records insert into the appropriate table via clickHouseService.insert

Error Handling and Graceful Degradation

The audit system implements defensive programming to prevent operational disruptions:

If ClickHouse is disabled (missing CLICKHOUSE_URL environment variable), the preventIfDisabled guard (lines 85‑90) short-circuits the operation and returns a success status without persisting data. Runtime insertion failures are captured as AuditException instances (defined in packages/twenty-server/src/engine/core-modules/audit/audit.exception.ts lines 23‑34) and logged without crashing the parent transaction.

Logging Custom Events

Developers can programmatically inject audit records for custom business logic using the public Audit Service API.

Recording Pageviews

To log user navigation patterns:

import { AuditService } from '@/engine/core-modules/audit/services/audit.service';

const audit = auditService.createContext({
  workspaceId: 'ws_123',
  userId: 'usr_456',
});

await audit.createPageviewEvent('Contact List', {
  url: '/contacts',
  referrer: '/dashboard',
});

This creates a pageview entry containing the standard common fields plus the supplied URL and referrer metadata.

Logging Object Mutations

For custom object lifecycle tracking:

import { OBJECT_RECORD_CREATED_EVENT } from '@/engine/core-modules/audit/utils/events/object-event/object-record-created';
import { AuditService } from '@/engine/core-modules/audit/services/audit.service';

const audit = auditService.createContext({
  workspaceId: 'ws_789',
  userId: 'usr_101',
});

await audit.createObjectEvent(OBJECT_RECORD_CREATED_EVENT, {
  recordId: 'rec_555',
  objectMetadataId: 'obj_meta_1',
  // Additional custom properties validated by Zod schema
});

This inserts a validated record into the objectEvent ClickHouse table with full audit context.

Summary

  • Twenty CRM audit logging captures workspace events, object record CRUD operations, and pageviews via the AuditService class
  • Every entry contains standardized fields (timestamp, version, workspaceId, userId) generated by common() in analytics.utils.ts
  • Three specialized writers (insertWorkspaceEvent, createObjectEvent, createPageviewEvent) persist data to distinct ClickHouse tables
  • Zod schemas in the events registry validate all payloads before insertion, ensuring schema consistency
  • The CreateAuditLogFromInternalEvent processor handles asynchronous batched writes to prevent request latency impact
  • Graceful degradation via preventIfDisabled ensures application stability when ClickHouse is unavailable

Frequently Asked Questions

What database does Twenty CRM use for audit logs?

Twenty CRM persists audit logs to ClickHouse, a columnar database optimized for analytical queries. The AuditService routes events to three specific tables: workspaceEvent, objectEvent, and pageview. If the CLICKHOUSE_URL environment variable is not configured, the system disables audit writes silently via the preventIfDisabled guard.

Which user actions are tracked in Twenty CRM audit logs?

The platform tracks object record CRUD operations (create, update, delete, upsert), workspace lifecycle events (creation, custom domain changes), user authentication (signups), logic function executions, and webhook responses. Each action type has a dedicated Zod schema in the eventsRegistry that defines its specific payload structure.

How does Twenty CRM validate audit log data?

All audit payloads undergo Zod schema validation before ClickHouse insertion. The makeTrackEvent and makePageview functions in the audit utilities validate event properties against schemas registered via registerEvent in track.ts. This ensures that fields like recordId and objectMetadataId conform to expected types and shapes.

Can audit logging be disabled in Twenty CRM?

Yes. If the CLICKHOUSE_URL environment variable is unset, the audit system automatically short-circuits via preventIfDisabled (lines 85‑90 in audit.service.ts) and returns success without writing data. This allows deployments to run without ClickHouse infrastructure while maintaining API compatibility.

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 →