Activity Log
Track changes and audit trail for records, emails, and automations
The Activity Log API provides a complete audit trail of all changes in your workspace. Track record modifications, email interactions, comments, and automation triggers.
Overview
Every action in Coherence is logged in the activity feed. Use the Activity Log API to:
- Build audit trails for compliance
- Display change history in your application
- Track user activity across the workspace
- Implement undo functionality
- Debug automation workflows
Activity logs are retained for 90 days on Standard plans and indefinitely on Enterprise plans.
Activity Types
The Activity Log tracks the following event types:
| Type | Description |
|---|---|
record.created | A new record was created |
record.updated | A record was modified |
record.deleted | A record was deleted |
field.changed | A specific field value changed (includes old/new values) |
email.sent | An email was sent from a record |
email.received | An email was received and linked to a record |
comment.added | A comment was added to a record |
automation.triggered | An automation ran on a record |
Activity Entry Structure
Each activity entry contains the following fields:
{
"id": "act_123",
"type": "record.updated",
"timestamp": "2024-01-15T10:30:00Z",
"user": {
"id": "usr_456",
"name": "John Smith",
"email": "[email protected]"
},
"record": {
"id": "rec_789",
"module": "contacts",
"name": "Acme Corp"
},
"changes": [
{
"field": "status",
"from": "active",
"to": "closed"
}
],
"metadata": {
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0..."
}
}Field Descriptions
| Field | Type | Description |
|---|---|---|
id | string | Unique activity identifier |
type | string | Activity type (see Activity Types) |
timestamp | string | ISO 8601 timestamp |
user | object | User who performed the action (null for system actions) |
record | object | Record that was affected |
changes | array | List of field changes (for update events) |
metadata | object | Additional context (IP, user agent, etc.) |
Get Record Activity
Retrieve the activity log for a specific record.
GET /modules/{module}/records/{id}/activity
Example Request
curl -X GET "https://api.getcoherence.io/v1/modules/contacts/records/rec_789/activity" \
-H "Authorization: Bearer YOUR_API_KEY"Example Response
{
"data": [
{
"id": "act_456",
"type": "field.changed",
"timestamp": "2024-01-15T14:22:00Z",
"user": {
"id": "usr_123",
"name": "Sarah Chen",
"email": "[email protected]"
},
"record": {
"id": "rec_789",
"module": "contacts",
"name": "Acme Corp"
},
"changes": [
{
"field": "deal_stage",
"from": "proposal",
"to": "negotiation"
}
]
},
{
"id": "act_455",
"type": "email.sent",
"timestamp": "2024-01-15T10:30:00Z",
"user": {
"id": "usr_123",
"name": "Sarah Chen",
"email": "[email protected]"
},
"record": {
"id": "rec_789",
"module": "contacts",
"name": "Acme Corp"
},
"metadata": {
"email_subject": "Follow-up on proposal",
"email_id": "eml_abc123"
}
},
{
"id": "act_400",
"type": "record.created",
"timestamp": "2024-01-10T09:00:00Z",
"user": {
"id": "usr_123",
"name": "Sarah Chen",
"email": "[email protected]"
},
"record": {
"id": "rec_789",
"module": "contacts",
"name": "Acme Corp"
}
}
],
"meta": {
"total": 3,
"page": 1,
"per_page": 25
}
}Get Module Activity
Retrieve activity for all records in a module.
GET /modules/{module}/activity
Example Request
curl -X GET "https://api.getcoherence.io/v1/modules/deals/activity?per_page=50" \
-H "Authorization: Bearer YOUR_API_KEY"Example Response
{
"data": [
{
"id": "act_789",
"type": "record.updated",
"timestamp": "2024-01-15T16:45:00Z",
"user": {
"id": "usr_456",
"name": "John Smith",
"email": "[email protected]"
},
"record": {
"id": "rec_deal_001",
"module": "deals",
"name": "Enterprise License - Acme"
},
"changes": [
{
"field": "value",
"from": 50000,
"to": 75000
}
]
},
{
"id": "act_788",
"type": "automation.triggered",
"timestamp": "2024-01-15T16:44:00Z",
"user": null,
"record": {
"id": "rec_deal_001",
"module": "deals",
"name": "Enterprise License - Acme"
},
"metadata": {
"automation_id": "auto_123",
"automation_name": "Deal Stage Notification"
}
}
],
"meta": {
"total": 156,
"page": 1,
"per_page": 50
}
}Get Workspace Activity
Retrieve activity across all modules in your workspace.
GET /activity
Example Request
curl -X GET "https://api.getcoherence.io/v1/activity" \
-H "Authorization: Bearer YOUR_API_KEY"Example Response
{
"data": [
{
"id": "act_900",
"type": "comment.added",
"timestamp": "2024-01-15T17:00:00Z",
"user": {
"id": "usr_789",
"name": "Alex Johnson",
"email": "[email protected]"
},
"record": {
"id": "rec_task_042",
"module": "tasks",
"name": "Review Q1 proposal"
},
"metadata": {
"comment_id": "cmt_abc",
"comment_preview": "I've reviewed the proposal and..."
}
},
{
"id": "act_899",
"type": "record.deleted",
"timestamp": "2024-01-15T16:55:00Z",
"user": {
"id": "usr_456",
"name": "John Smith",
"email": "[email protected]"
},
"record": {
"id": "rec_contact_old",
"module": "contacts",
"name": "Duplicate Entry"
}
}
],
"meta": {
"total": 1250,
"page": 1,
"per_page": 25
}
}Filtering Activity
Filter activity logs using query parameters.
Filter Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
type | string | Filter by activity type | ?type=record.updated |
types | string | Filter by multiple types (comma-separated) | ?types=record.created,record.updated |
user_id | string | Filter by user | ?user_id=usr_123 |
from | string | Start date (ISO 8601) | ?from=2024-01-01T00:00:00Z |
to | string | End date (ISO 8601) | ?to=2024-01-31T23:59:59Z |
field | string | Filter by changed field | ?field=status |
Filter by Date Range
curl -X GET "https://api.getcoherence.io/v1/modules/contacts/activity?from=2024-01-01T00:00:00Z&to=2024-01-31T23:59:59Z" \
-H "Authorization: Bearer YOUR_API_KEY"Filter by User
curl -X GET "https://api.getcoherence.io/v1/activity?user_id=usr_123" \
-H "Authorization: Bearer YOUR_API_KEY"Filter by Activity Type
curl -X GET "https://api.getcoherence.io/v1/activity?types=record.created,record.deleted" \
-H "Authorization: Bearer YOUR_API_KEY"Filter by Field Changed
curl -X GET "https://api.getcoherence.io/v1/modules/deals/activity?field=deal_stage" \
-H "Authorization: Bearer YOUR_API_KEY"Combining Filters
curl -X GET "https://api.getcoherence.io/v1/modules/deals/activity?type=field.changed&field=status&from=2024-01-01T00:00:00Z&user_id=usr_123" \
-H "Authorization: Bearer YOUR_API_KEY"Pagination
Activity feeds support cursor-based pagination for efficient navigation through large datasets.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
cursor | string | - | Cursor for next page |
Example with Pagination
curl -X GET "https://api.getcoherence.io/v1/activity?per_page=50&page=2" \
-H "Authorization: Bearer YOUR_API_KEY"Response with Pagination
{
"data": [...],
"meta": {
"total": 1250,
"page": 2,
"per_page": 50,
"total_pages": 25,
"next_cursor": "eyJpZCI6ImFjdF81MDAifQ==",
"prev_cursor": "eyJpZCI6ImFjdF80MDAifQ=="
}
}Cursor-Based Pagination
For real-time feeds, use cursor-based pagination:
curl -X GET "https://api.getcoherence.io/v1/activity?cursor=eyJpZCI6ImFjdF81MDAifQ==" \
-H "Authorization: Bearer YOUR_API_KEY"Cursor-based pagination is more efficient for large datasets and ensures you don't miss entries when new activity is added.
Use Cases
Audit Trail
Build compliance-ready audit logs by fetching all activity for a record:
async function getAuditTrail(moduleSlug: string, recordId: string) {
const response = await fetch(
`https://api.getcoherence.io/v1/modules/${moduleSlug}/records/${recordId}/activity`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
const { data } = await response.json();
return data.map(entry => ({
timestamp: entry.timestamp,
action: entry.type,
user: entry.user?.name || 'System',
changes: entry.changes
}));
}Change Tracking
Monitor specific field changes across your workspace:
async function trackFieldChanges(field: string, startDate: string) {
const response = await fetch(
`https://api.getcoherence.io/v1/activity?type=field.changed&field=${field}&from=${startDate}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
const { data } = await response.json();
return data.map(entry => ({
record: entry.record.name,
module: entry.record.module,
oldValue: entry.changes[0].from,
newValue: entry.changes[0].to,
changedBy: entry.user.name,
changedAt: entry.timestamp
}));
}Undo Support
Use the activity log to implement undo functionality:
async function undoLastChange(moduleSlug: string, recordId: string) {
// Get the most recent change
const response = await fetch(
`https://api.getcoherence.io/v1/modules/${moduleSlug}/records/${recordId}/activity?type=field.changed&per_page=1`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
const { data } = await response.json();
if (data.length === 0) {
throw new Error('No changes to undo');
}
const lastChange = data[0];
// Revert the changes
const revertData = {};
for (const change of lastChange.changes) {
revertData[change.field] = change.from;
}
// Update the record with previous values
await fetch(
`https://api.getcoherence.io/v1/modules/${moduleSlug}/records/${recordId}`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(revertData)
}
);
}User Activity Dashboard
Display recent activity for a specific user:
async function getUserActivity(userId: string, days: number = 7) {
const fromDate = new Date();
fromDate.setDate(fromDate.getDate() - days);
const response = await fetch(
`https://api.getcoherence.io/v1/activity?user_id=${userId}&from=${fromDate.toISOString()}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
const { data } = await response.json();
// Group by date
const grouped = data.reduce((acc, entry) => {
const date = entry.timestamp.split('T')[0];
if (!acc[date]) acc[date] = [];
acc[date].push(entry);
return acc;
}, {});
return grouped;
}Error Responses
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | invalid_date_range | Invalid date format or range |
| 401 | unauthorized | Missing or invalid API key |
| 403 | forbidden | Insufficient permissions to view activity |
| 404 | record_not_found | Record or module not found |
| 429 | rate_limited | Too many requests |
Example Error Response
{
"error": {
"code": "invalid_date_range",
"message": "The 'from' date must be before the 'to' date",
"status": 400
}
}Related: API Overview | Records API | Webhooks