Audit API
The audit router provides read access to the tenant's audit log. All mutations across the platform are automatically recorded, creating a compliance-ready trail of changes. Access is restricted to admin users.
Router namespace: audit
Source: src/server/trpc/routers/audit.ts
Access: All procedures require admin role.
Procedures
| Procedure | Type | Access | Description |
|---|---|---|---|
list | query | admin | Paginated audit log with cursor-based pagination |
getByResource | query | admin | Filter audit entries by resource type and optional ID |
list
Returns a paginated list of audit log entries ordered by most recent first. Uses cursor-based pagination for efficient traversal of large logs.
audit.list.queryOptions(input)
Input
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number (1-100) | No | 50 | Maximum entries per page |
cursor | string | No | -- | ISO 8601 timestamp cursor from previous page |
Response
{
"items": [
{
"id": "aud_abc123",
"tenantId": "tenant_789",
"userId": "user_001",
"action": "settings_change",
"resource": "scoring_config",
"resourceId": null,
"metadata": {
"configType": "combined",
"version": 3
},
"createdAt": "2026-03-05T10:30:00.000Z"
},
{
"id": "aud_def456",
"tenantId": "tenant_789",
"userId": "user_002",
"action": "connector_sync",
"resource": "connector",
"resourceId": "conn_456",
"metadata": {
"syncType": "incremental",
"recordsCreated": 150
},
"createdAt": "2026-03-05T08:00:00.000Z"
}
],
"nextCursor": "2026-03-05T08:00:00.000Z"
}
When nextCursor is null, there are no more pages.
Cursor-Based Pagination
Unlike offset-based pagination, cursor pagination provides consistent results even as new entries are added. Pass the nextCursor value from the previous response as the cursor input for the next page:
const trpc = useTRPC();
// First page
const page1 = useQuery(
trpc.audit.list.queryOptions({ limit: 25 })
);
// Next page (when user clicks "Load More")
const page2 = useQuery(
trpc.audit.list.queryOptions({
limit: 25,
cursor: page1.data?.nextCursor ?? undefined,
}),
{ enabled: !!page1.data?.nextCursor }
);
Use TanStack Query's useInfiniteQuery for infinite scroll patterns with the cursor from nextCursor.
getByResource
Filters audit log entries by resource type and optionally by a specific resource ID. Returns up to 100 entries ordered by most recent first.
audit.getByResource.queryOptions(input)
Input
| Field | Type | Required | Description |
|---|---|---|---|
resource | string | Yes | Resource type (e.g., "scoring_config", "connector") |
resourceId | string | No | Specific resource identifier |
Response
Returns the same item structure as list, without pagination:
[
{
"id": "aud_abc123",
"tenantId": "tenant_789",
"userId": "user_001",
"action": "settings_change",
"resource": "scoring_config",
"resourceId": null,
"metadata": {
"configType": "fit",
"version": 2
},
"createdAt": "2026-03-05T10:30:00.000Z"
}
]
Example
const trpc = useTRPC();
// All scoring config changes
const { data: configChanges } = useQuery(
trpc.audit.getByResource.queryOptions({
resource: "scoring_config",
})
);
// Changes for a specific connector
const { data: connectorHistory } = useQuery(
trpc.audit.getByResource.queryOptions({
resource: "connector",
resourceId: "conn_456",
})
);
Audit Event Structure
Every audit log entry contains these fields:
| Field | Type | Description |
|---|---|---|
id | string | Unique audit entry identifier |
tenantId | string | Tenant scope |
userId | string | Clerk user ID who performed the action |
action | string | Action type (see table below) |
resource | string | Resource type affected |
resourceId | string | null | Specific resource identifier (if applicable) |
metadata | Record<string, unknown> | Action-specific details |
createdAt | Date | When the event occurred |
Common Action Types
| Action | Resource | Description |
|---|---|---|
settings_change | scoring_config | Scoring configuration updated |
connector_sync | connector | Data sync triggered |
connector_disconnect | connector | Connector disconnected |
writeback_trigger | writeback | Score writeback initiated |
role_confirmed | buying_group_member | Buying group role manually confirmed |
settings_change | tenant | Tenant settings updated |
Audit entries are created automatically by tRPC middleware on all mutations. You do not need to manually log actions -- the middleware handles this.
Related Pages
- Scoring Config API -- Config changes appear in audit log
- Authentication -- Admin role required for audit access
- Error Handling --
FORBIDDENif non-admin attempts access