Events
This page documents the Events endpoints in the Event History API microservice.
The Event History service is used to store and retrieve historical activity for a client environment. During client creation in the main Autologyx system, a client-specific ClickHouse database is created for history data, and a corresponding tenant entry is created in the default ClickHouse database. The tenant entry is used to resolve the correct client database from the client_id.
WARNING
This section documents technical and service-level Event History APIs and is intended for internal platform use and controlled engineering workflows. It should not be treated as part of the main general-purpose external Catalyst integration surface. This area is outside general support coverage, so please contact your Customer Success team if you need guidance.
API overview
This page covers the following endpoints:
POST /api/event-bridge/create-eventPOST /api/system-events/batchGET /api/history/eventsGET /api/history/events/event_id/file
POST /api/event-bridge/create-event
Creates a History Event record in ClickHouse through the AWS EventBridge → Lambda → Event History API flow.
Permissions
Requires authentication.
Request
Headers
Request Method: POST
Content-Type: application/json
X-API-Key: API_KEYBody schema
{
"version": "string",
"id": "string",
"detail-type": "string",
"source": "string",
"account": "string",
"time": "2022-11-10T10:26:12.111Z",
"region": "string",
"resources": [],
"detail": {
"event_id": "string",
"event_time": "2022-11-10T10:26:12.111Z",
"event_type_id": 1,
"record_id": 0,
"source_id": 0,
"source_type_id": 0,
"event_data": [
"string"
]
}
}Response
Success
- Status:
201 Created
{
"detail": "<string>"
}Filtering
No filters are supported.
Errors
| Error | Response code | Message |
|---|---|---|
| Tenant not found | 400 Bad Request | "detail": "'{tenant}' tenant not found in database." |
| Wrong API key | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Missing authorization header | 401 Unauthorized | "detail": "Unauthorized API Key." |
POST /api/system-events/batch
Overview
Inserts a batch of System Events into the tenant-specific system_events table in ClickHouse.
Intended producers are internal platform services that emit lifecycle, authentication, or configuration events (for example the Catalyst backend or the auth service) and need a high-throughput, idempotent write path. Records are routed to the per-tenant ClickHouse database resolved from the X-Client-Id header, and persisted to the system_events table in that database.
Authentication
Both headers are required.
| Header | Notes |
|---|---|
X-API-Key | Service API key. Same key shape as the rest of the Event History API. |
X-Client-Id | Tenant identifier (UUID v4). Used to resolve the destination ClickHouse database. |
Request
Headers
Request Method: POST
Content-Type: application/json
X-API-Key: API_KEY
X-Client-Id: client_idBody schema
{
"events": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"event_timestamp": "2026-06-19T10:00:00.000000Z",
"category": 1,
"event_type": 1,
"source_id": 42,
"source_type": 1,
"source_info": "browser",
"source_ip": "1.2.3.4",
"event_data": "{\"auth_type\": \"PW\"}"
}
]
}Event fields
| Key | Type | Required | Default | Notes |
|---|---|---|---|---|
| id | uuid | Yes | — | Event identifier. Idempotency key — see Idempotency contract. |
| event_timestamp | datetime | Yes | — | ISO 8601 UTC timestamp with microsecond precision. |
| category | enum (int) | Yes | — | Event category. See Category values. |
| event_type | enum (int) | Yes | — | Event type within the category. See Event type values. |
| source_id | int | No | 0 | Identifier of the source entity (e.g. user ID). |
| source_type | enum (int) | No | 0 | Source type. See Source type values. |
| source_info | string | No | "" | Free-form metadata about the source (e.g. user agent label). |
| source_ip | string | No | "" | Origin IP address. |
| event_data | string | No | "" | JSON-encoded event payload. Schema is event-specific and is stored as a raw string. |
Category values
Defined by the backend EventCategories enum.
| Value | Name | Description |
|---|---|---|
1 | auth | Authentication-related events. |
Event type values
Defined by the backend SystemEventTypes enum. Values are scoped by category — currently all defined event types belong to category = 1 (auth).
| Value | Name | Description |
|---|---|---|
1 | login_success | Successful login. |
2 | login_failed | Login failed (generic). |
3 | login_failed_sso | Login via SSO failed. |
4 | login_failed_inactive_account | Login rejected because the account is inactive. |
5 | login_failed_invalid_auth_method | Login rejected because the auth method is not allowed for the account. |
6 | account_locked | Account was locked (e.g. after repeated failed logins). |
7 | password_reset_request_success | Password reset request submitted successfully. |
8 | password_reset_request_failed | Password reset request failed. |
9 | password_reset_success | Password reset completed successfully. |
10 | password_reset_failed | Password reset attempt failed. |
Source type values
Defined by the backend SourceTypes enum (shared with the History Events API).
| Value | Name | Description |
|---|---|---|
1 | user | Event originated from a user action. |
2 | sequence | Event originated from an automated sequence. |
3 | system | Event originated from the platform itself. |
4 | mcp | Event originated from an MCP integration. |
Response
Success
- Status:
201 Created
{
"inserted": 1
}| Key | Type | Notes |
|---|---|---|
| inserted | int | Number of rows actually written to system_events after deduplication. May be lower than the batch size if some ids were duplicates within the batch or already present in the table. |
Idempotency contract
The endpoint provides at-most-once write semantics keyed by id:
- Intra-batch dedup — If the same
idappears more than once in a single request, only the first occurrence is written; the rest are skipped silently. - Cross-batch dedup — If an
idis already present in thesystem_eventstable, the incoming row is skipped silently. - Reported count — The
insertedvalue reflects rows actually persisted after both dedup steps.
ClickHouse MergeTree caveat
Deduplication is enforced on the write path by checking id against the bloom filter index and the table. ClickHouse MergeTree does not enforce primary key uniqueness on its own, so producers must not rely on the database to reject duplicates outside of this endpoint — any direct INSERT bypassing this API can introduce duplicate ids.
Limits
| Limit | Default | Configuration |
|---|---|---|
| Maximum events per request | 10000 | SYSTEM_EVENTS_BATCH_MAX_SIZE environment variable. |
Requests containing more than the configured maximum are rejected with 422 Unprocessable Entity.
Errors
| Error | Response code | Message |
|---|---|---|
| Wrong API key | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Missing authorization header | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Tenant not found | 400 Bad Request | "detail": "'{tenant}' tenant not found in database." |
| Client ID header is missing | 400 Bad Request | "detail": "Unauthorized Client." |
| Client from header does not match any tenant | 400 Bad Request | "detail": "Failed to connect to tenant db." |
| Malformed JSON body | 400 Bad Request | "detail": "Invalid JSON body." |
| Batch exceeds size limit | 422 Unprocessable Entity | {"detail": [{"loc": ["body", "events"], "msg": "ensure this value has at most 10000 items", "type": "value_error.list.max_items", "ctx": {"limit_value": 10000}}]} |
| Missing required field on an event | 422 Unprocessable Entity | {"detail": [{"loc": ["body", "events", 0, "id"], "msg": "field required", "type": "value_error.missing"}]} |
Invalid id (not a UUID) | 422 Unprocessable Entity | {"detail": [{"loc": ["body", "events", 0, "id"], "msg": "value is not a valid uuid", "type": "type_error.uuid"}]} |
Invalid event_timestamp format | 422 Unprocessable Entity | {"detail": [{"loc": ["body", "events", 0, "event_timestamp"], "msg": "invalid datetime format", "type": "value_error.datetime"}]} |
Invalid category / event_type / source_type value | 422 Unprocessable Entity | {"detail": [{"loc": ["body", "events", 0, "category"], "msg": "value is not a valid enumeration member", "type": "type_error.enum"}]} |
| ClickHouse write failure after retries | 500 Internal Server Error | "detail": "Failed to persist system events." |
| Upstream ClickHouse unavailable | 503 Service Unavailable | "detail": "ClickHouse temporarily unavailable." |
Example
Request
curl -X POST https://<host>/api/system-events/batch \
-H "Content-Type: application/json" \
-H "X-API-Key: API_KEY" \
-H "X-Client-Id: 1a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d" \
-d '{
"events": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"event_timestamp": "2026-06-19T10:00:00.000000Z",
"category": 1,
"event_type": 1,
"source_id": 42,
"source_type": 1,
"source_info": "browser",
"source_ip": "1.2.3.4",
"event_data": "{\"auth_type\": \"PW\"}"
}
]
}'Response
- Status:
201 Created
{
"inserted": 1
}Operational notes
- Bloom filter index on
id— Thesystem_eventstable carries a bloom filter secondary index on theidcolumn to keep the cross-batch dedup lookup cheap. Producers should treatidas a stable, high-entropy UUID v4 so the filter retains its selectivity. async_insertenabled — Writes use ClickHouseasync_insert, so the201response is returned once the batch is accepted into the server-side buffer, not once it is fully merged into the part. Reads issued immediately after a successful write may not see the new rows until the buffer flushes.- Retry on
ServerError— The endpoint retries transient ClickHouseServerErrorresponses up to 5 attempts with exponential backoff before surfacing a500/503to the caller. Idempotency byidmakes these retries safe.
GET /api/history/events
Returns a paginated list of History Events based on query parameters.
Permissions
Requires authentication and client_id in the request header.client_id must be a UUID v4 value.
Request
Headers
Request Method: GET
Content-Type: application/json
X-API-Key: API_KEY
X-Client-Id: client_idResponse
Top-level response
| Key | Type | Notes |
|---|---|---|
| limit | int | Page size |
| offset | int | Page offset |
| total_count | int | Total number of history events visible to the API client |
| filtered_count | int | Number of events returned after filters are applied |
| results | Array | Event records |
results array fields
| Key | Type | Notes |
|---|---|---|
| event_id | uuid | Event identifier |
| event_datetime | datetime | Event timestamp |
| event_type | enum | Values include record_created, owner_initialized, owners_added, owners_removed, assignees_added, assignees_removed, field_values_changed, record_deleted, document_generated, status_initialized |
| event_data | json | Schema depends on event_type |
| source_type | enum | Values: user, sequence, system, mcp |
| source_id | int | Source identifier |
| source_name | string | Source display name. For source_type: mcp, resolved from the users table by source_id. Returns null if the source_id does not exist in the users table. |
| source_info | string | Additional information about the event source. For MCP events, contains the AI model identifier (e.g. "claude-code 2.1.158"). Returns "" for all other event types. |
results.event_data by event type
The structure of event_data depends on the event_type.
owner_initialized, owners_added, owners_removed
Returns an array of owner objects.
| Key | Type | Notes |
|---|---|---|
| id | int | User ID |
| type | enum | Value: user |
| name | string | User label |
[
{
"id": 9768,
"type": "user",
"name": "John Smith"
}
]assignees_added, assignees_removed
Returns an array of assignee objects.
| Key | Type | Notes |
|---|---|---|
| id | int | User ID |
| type | enum | Value: user |
| name | string | User label |
| permission_set | Object | Permission Set object |
permission_set
| Key | Type | Notes |
|---|---|---|
| id | int | Permission Set ID |
| name | string | Permission Set label |
[
{
"id": 9768,
"type": "user",
"name": "John Smith",
"permission_set": {
"id": 35,
"name": "PermSet"
}
}
]field_values_changed, record_created
Returns an array of field-value objects.
| Key | Type | Notes |
|---|---|---|
| field_id | int | Field ID |
| field_type | enum | Field type |
| field_value | Any | Any JSON-compatible field value |
| field_name | string | Field label |
[
{
"field_id": 9768,
"field_type": "string",
"field_value": "Value",
"field_name": "Car"
}
]record_deleted
Returns an empty array.
[]document_generated
The source page indicates this event contains document-related payload data. A representative example is:
[
{
"document_template_id": 123,
"field_id": 12,
"file_id": "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p"
}
]Response examples
Example: record_deleted
- Status:
200 OK
{
"limit": 1,
"offset": 0,
"filtered_count": 2,
"total_count": 4,
"results": [
{
"event_id": "6e165f4b-8729-487c-93eb-7bf3445554e9",
"event_datetime": "2021-10-27T12:27:43.462803+00:00",
"event_type": "record_deleted",
"source_type": "mcp",
"source_id": 6,
"source_name": null,
"source_info": "claude-code 2.1.158",
"event_data": []
}
]
}Example: document_generated
- Status:
200 OK
{
"limit": 1,
"offset": 0,
"filtered_count": 2,
"total_count": 4,
"results": [
{
"event_id": "6e165f4b-8729-487c-93eb-7bf3445554e9",
"event_datetime": "2021-10-27T12:27:43.462803+00:00",
"event_type": "document_generated",
"event_data": [
{
"document_template_id": 123,
"field_id": 12,
"file_id": "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p"
}
],
"source_type": "user",
"source_id": 1
}
]
}Sorting
Sorting cannot be controlled through the request.
Results are returned in descending order by event_datetime. No other sorting is supported.
Notes:
record_createdis the first event in chronological order, so it is sorted at the end of the descending result set.owner_initializedimmediately followsrecord_createdin chronological order.
Filtering
For the full description of standard predicates, see ALX API standards - Developers guide.
| Parameter | Example |
|---|---|
| event_datetime | /api/history/events?event_datetime=2022-01-01 |
| event_datetime! | /api/history/events?event_datetime__exclude=2022-01-01 |
| event_datetime_gt | /api/history/events?event_datetime__gt=2022-01-01 |
| event_datetime_gt! | /api/history/events?event_datetime__gt__exclude=2022-01-01 |
| event_datetime_gte | /api/history/events?event_datetime__gte=2022-01-01 |
| event_datetime_gte! | /api/history/events?event_datetime__gte__exclude=2022-01-01 |
| event_datetime_range | /api/history/events?event_datetime__range=2022-01-01,2022-02-01 |
| event_datetime_range! | /api/history/events?event_datetime__range__exclude=2022-01-01,2022-02-01 |
| record_id | /api/history/events?record_id=1 |
| source_type | /api/history/events?source_type=user |
| source_type! | /api/history/events?source_type__exclude=sequence |
| source_type__in | /api/history/events?source_type__in=system,user |
| source_type__in! | /api/history/events?source_type__in__exclude=sequence |
| source_id | /api/history/events?source_id=user |
| source_id! | /api/history/events?source_id__exclude=sequence |
| source_id__in | /api/history/events?source_id__in=system,user |
| source_id__in! | /api/history/events?source_id__in__exclude=sequence |
| source_info | /api/history/events?source_info=claude-code+2.1.158 |
| source_info__exclude | /api/history/events?source_info__exclude=claude-code+2.1.158 |
| source_info__in | /api/history/events?source_info__in=claude-code+2.1.158,cursor+1.0.0 |
| source_info__in__exclude | /api/history/events?source_info__in__exclude=claude-code+2.1.158 |
| field_id | /api/history/events?field_id=1 |
| field_id__in | /api/history/events?field_id__in=121,122,150 |
| field_id__in! | /api/history/events?field_id__in__exclude=121,122 |
field_id__in returns events whose field_id matches any value in the comma-separated list; field_id__in__exclude returns events whose field_id matches none of the listed values. Both variants only apply to events that actually carry a field_id (currently field_values_changed); they combine with all other filters (record_id, event_type__in, source_*, event_datetime__*) via AND. Non-integer values in the CSV are silently dropped; an all-invalid CSV skips the filter entirely.
Notes on source_info filters
- Filtering is case-sensitive (ClickHouse compares byte-by-byte).
source_info(exact match) accepts an empty string""— filters for events without metadata (i.e. non-MCP events).source_info__inignores empty entries (e.g. a trailing comma value is treated as a value).
Errors
| Error | Response code | Message |
|---|---|---|
| Tenant not found | 400 Bad Request | "detail": "'{tenant}' tenant not found in database." |
| Source type invalid | 400 Bad Request | {'detail': [{'loc': ['query', 'source_type'], 'msg': "value is not a valid enumeration member; permitted: 'user', 'sequence', 'system', 'mcp'", 'type': 'type_error.enum', 'ctx': {'enum_values': ['user', 'sequence', 'system', 'mcp']}}]} |
| Source ID invalid | 400 Bad Request | {"detail": [{"loc": ["query", "source_id"], "msg": "value is not a valid integer", "type": "type_error.integer"}]} |
| Field ID invalid | 400 Bad Request | {"detail": [{"loc": ["query", "field_id"], "msg": "value is not a valid integer", "type": "type_error.integer"}]} |
| Client ID header is missing | 400 Bad Request | "detail": "Unauthorized Client." |
| Client from header does not match any tenant | 400 Bad Request | "detail": "Failed to connect to tenant db." |
| Wrong API key | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Missing authorization header | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Any request method other than GET is used | 405 Method Not Allowed | |
| Event datetime range wrong format - too many items | 422 Unprocessable Entity | {'detail': [{'loc': ['range_value'], 'msg': 'ensure this value has at most 2 items', 'type': 'value_error.list.max_items', 'ctx': {'limit_value': 2}}]} |
| Event datetime range wrong format - too few items | 422 Unprocessable Entity | {'detail': [{'loc': ['range_value'], 'msg': 'ensure this value has at least 2 items', 'type': 'value_error.list.min_items', 'ctx': {'limit_value': 2}}]} |
| Event datetime range wrong format on range item | 422 Unprocessable Entity | {'detail': [{'loc': ['range_value', 0], 'msg': 'invalid datetime format', 'type': 'value_error.datetime'}, {'loc': ['range_value', 0], 'msg': 'invalid date format', 'type': 'value_error.date'}]} |
GET /api/history/events/event_id/file
Generates a presigned URL for downloading a file from S3.
The client instance is identified through the X-Client-Id header.
Permissions
Requires authentication.
Request
Headers
Request Method: GET
Content-Type: application/force-download
X-API-Key: API_KEY
X-Client-Id: client_idExample path
GET /api/history/events/1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p/fileErrors
| Error | Response code | Message |
|---|---|---|
| Tenant not found | 400 Bad Request | "detail": "'{tenant}' tenant not found in database." |
| Client ID header is missing | 400 Bad Request | "detail": "Unauthorized Client." |
| Client from header does not match any tenant | 400 Bad Request | "detail": "Failed to connect to tenant db." |
| Wrong API key | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Missing authorization header | 401 Unauthorized | "detail": "Unauthorized API Key." |
| Any request method other than GET is used | 405 Method Not Allowed | |
| Event not found | 404 Not Found | "detail": "Event not found." |