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

> Explore Twenty CRM's audit logging technical details. Discover how workspace events, record changes, and pageviews are captured and stored in ClickHouse with user context and timestamps.

- Repository: [Twenty/twenty](https://github.com/twentyhq/twenty)
- Tags: deep-dive
- Published: 2026-03-27

---

**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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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`:

```typescript
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`](https://github.com/twentyhq/twenty/blob/main/audit.service.ts) lines 42‑46 |
| `createObjectEvent` | `objectEvent` | [`audit.service.ts`](https://github.com/twentyhq/twenty/blob/main/audit.service.ts) lines 47‑71 |
| `createPageviewEvent` | `pageview` | [`audit.service.ts`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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 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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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:

```typescript
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:

```typescript
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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/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`](https://github.com/twentyhq/twenty/blob/main/audit.service.ts)) and returns success without writing data. This allows deployments to run without ClickHouse infrastructure while maintaining API compatibility.