Skip to main content
A project management tool like Asana or Linear needs a complete record of who did what and when. Immutable lets you track every action — from creating projects to completing tasks — with full actor context, session continuity, and multi-resource targeting. Your customers can even view their own activity feed via an embeddable viewer.

Setting Up the Client

import { ImmutableClient } from "getimmutable";

const client = new ImmutableClient({
  apiKey: "imk_sk_a1b2c3d4e5f6g7h8i9j0",
  baseUrl: "https://getimmutable.dev",
});

Tracking the Full Project Lifecycle

User Creates a Project

await client
  .actor("user_8f3k2", { name: "Sarah Chen", type: "user" })
  .session("sess_29dk3f8a")
  .track("project.created", "project", {
    project_id: "proj_4a9f2c",
    project_name: "Q3 Marketing Campaign",
    visibility: "team",
    template: "marketing-launch",
  });

User Adds a Task with Targets

When a task is created inside a project, use targets to link the event to both the project and the task:
await client
  .actor("user_8f3k2", { name: "Sarah Chen", type: "user" })
  .session("sess_29dk3f8a")
  .track("task.created", "task", {
    task_id: "task_7b2e9d",
    task_title: "Design landing page mockup",
    priority: "high",
    due_date: "2026-04-15",
    targets: [
      { type: "project", id: "proj_4a9f2c", name: "Q3 Marketing Campaign" },
      { type: "task", id: "task_7b2e9d", name: "Design landing page mockup" },
    ],
  });

User Assigns a Team Member

await client
  .actor("user_8f3k2", { name: "Sarah Chen", type: "user" })
  .session("sess_29dk3f8a")
  .track("task.member_assigned", "task", {
    task_id: "task_7b2e9d",
    assignee_id: "user_3m7p1q",
    assignee_name: "Marcus Rivera",
    targets: [
      { type: "project", id: "proj_4a9f2c", name: "Q3 Marketing Campaign" },
      { type: "task", id: "task_7b2e9d", name: "Design landing page mockup" },
      { type: "user", id: "user_3m7p1q", name: "Marcus Rivera" },
    ],
  });

Task Completed and Project Archived

// Task completed by the assignee
await client
  .actor("user_3m7p1q", { name: "Marcus Rivera", type: "user" })
  .session("sess_44bf9c2e")
  .track("task.completed", "task", {
    task_id: "task_7b2e9d",
    time_spent_hours: 12.5,
    targets: [
      { type: "project", id: "proj_4a9f2c", name: "Q3 Marketing Campaign" },
      { type: "task", id: "task_7b2e9d", name: "Design landing page mockup" },
    ],
  });

// Project archived by the manager
await client
  .actor("user_8f3k2", { name: "Sarah Chen", type: "user" })
  .session("sess_29dk3f8a")
  .track("project.archived", "project", {
    project_id: "proj_4a9f2c",
    total_tasks: 24,
    completed_tasks: 24,
  });

Querying Activity by Actor

Retrieve all actions performed by a specific user:
const events = await client.getEvents({
  actor_id: "user_8f3k2",
  limit: 50,
});

events.data.forEach((event) => {
  console.log(`${event.occurred_at}${event.action} on ${event.resource_name}`);
});

Building a Customer-Facing Activity Feed

Create a viewer token scoped to a tenant so customers can see their own team’s activity:
const token = await client.createViewerToken({
  tenantId: "org_acme_corp",
  ttl: 3600,
});

// Pass token.viewer_token to your frontend
// Embed the viewer iframe or query the public events endpoint
console.log(token.viewer_token);
Viewer tokens are scoped by tenant_id, so each customer organization only sees their own events. Set a TTL that matches your frontend session length.

What’s Next

Targets

Learn how targets link events to multiple resources.

Session Tracking

Group events into user sessions for richer context.

Embeddable Viewer

Build a customer-facing activity feed with viewer tokens.

Customer-Facing Activity Feed

Full guide to multi-tenant activity feeds.