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 asyyyy-MM-dd HH:mm:ssvia thecommon()helper inpackages/twenty-server/src/engine/core-modules/audit/utils/analytics.utils.tsversion– Schema version identifier (currently'1')workspaceId– The workspace identifier supplied viacreateContextuserId– The user identifier who triggered the actiontype– Either'track'for action events or'page'for pageviewsevent– 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:
- OBJECT_RECORD_CREATED_EVENT – Defined in
packages/twenty-server/src/engine/core-modules/audit/utils/events/object-event/object-record-created.ts(lines 5‑15) - OBJECT_RECORD_UPDATED_EVENT – Defined in
packages/twenty-server/src/engine/core-modules/audit/utils/events/object-event/object-record-updated.ts(lines 5‑15) - OBJECT_RECORD_DELETED_EVENT – Tracks deletions with record metadata
- OBJECT_RECORD_UPSERTED_EVENT – Captures upsert operations
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 provisioningUSER_SIGNUP_EVENT– User registration eventsCUSTOM_DOMAIN_ACTIVATED_EVENT/CUSTOM_DOMAIN_DEACTIVATED_EVENT– Domain configuration changesLOGIC_FUNCTION_EXECUTED_EVENT– Serverless function invocationsWEBHOOK_RESPONSE_EVENT– External integration callbacksMONITORING_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:
- Domain Event Emission – Workspace operations emit events via the workspace-event emitter
- Queue Processing – The
CreateAuditLogFromInternalEventprocessor (packages/twenty-server/src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event.ts, lines 13‑62) receives batched events - Context Initialization – The processor creates an audit context binding workspace and user identifiers
- Schema Validation –
makeTrackEventvalidates payloads against Zod schemas from the events registry - 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
AuditServiceclass - Every entry contains standardized fields (
timestamp,version,workspaceId,userId) generated bycommon()inanalytics.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
CreateAuditLogFromInternalEventprocessor handles asynchronous batched writes to prevent request latency impact - Graceful degradation via
preventIfDisabledensures 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →