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:

TypeDescription
record.createdA new record was created
record.updatedA record was modified
record.deletedA record was deleted
field.changedA specific field value changed (includes old/new values)
email.sentAn email was sent from a record
email.receivedAn email was received and linked to a record
comment.addedA comment was added to a record
automation.triggeredAn 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

FieldTypeDescription
idstringUnique activity identifier
typestringActivity type (see Activity Types)
timestampstringISO 8601 timestamp
userobjectUser who performed the action (null for system actions)
recordobjectRecord that was affected
changesarrayList of field changes (for update events)
metadataobjectAdditional 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

ParameterTypeDescriptionExample
typestringFilter by activity type?type=record.updated
typesstringFilter by multiple types (comma-separated)?types=record.created,record.updated
user_idstringFilter by user?user_id=usr_123
fromstringStart date (ISO 8601)?from=2024-01-01T00:00:00Z
tostringEnd date (ISO 8601)?to=2024-01-31T23:59:59Z
fieldstringFilter 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

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger25Results per page (max 100)
cursorstring-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

StatusCodeDescription
400invalid_date_rangeInvalid date format or range
401unauthorizedMissing or invalid API key
403forbiddenInsufficient permissions to view activity
404record_not_foundRecord or module not found
429rate_limitedToo 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