Skip to main content

Overview

Teams in Inbox organize your account links, prospects, and conversations. This guide covers retrieving team information and managing team members via the API.

Team Data Model

interface Team {
  id: string;
  name: string;
  createdAt: string;
  plan: 'starter' | 'professional' | 'enterprise';
}

interface Member {
  id: string;
  email: string;
  name: string;
  role: 'owner' | 'admin' | 'member';
  createdAt: string;
}

Get Team Information

Retrieve your team details:
const { data } = await client.get('/team');

console.log('Team:', data.name);
console.log('Plan:', data.plan);
console.log('Created:', data.createdAt);
Response:
{
  "id": "team_abc123",
  "name": "Acme Corp",
  "createdAt": "2024-01-15T10:30:00.000Z",
  "plan": "enterprise"
}

List Team Members

Get all members of your team:
const { data } = await client.get('/members');

console.log(`Team has ${data.members.length} members`);
data.members.forEach(member => {
  console.log(`${member.name} (${member.email}) - ${member.role}`);
});
Response:
{
  "members": [
    {
      "id": "member_owner",
      "email": "owner@acme.com",
      "name": "Jane Smith",
      "role": "owner",
      "createdAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "member_sales",
      "email": "sales@acme.com",
      "name": "John Doe",
      "role": "admin",
      "createdAt": "2024-02-01T09:00:00.000Z"
    },
    {
      "id": "member_support",
      "email": "support@acme.com",
      "name": "Alice Johnson",
      "role": "member",
      "createdAt": "2024-02-15T14:00:00.000Z"
    }
  ]
}

Get Specific Member

Retrieve details for a specific member:
const { data } = await client.get(`/members/${memberId}`);
console.log('Member:', data.member.name);

Member Roles

RolePermissions
ownerFull access, billing management, team deletion
adminFull access except billing and team deletion
memberAccess to assigned threads and features

Thread Assignment

Assign threads to team members for workload distribution:
// Assign a thread to a member
await client.patch(`/threads/${threadId}`, {
  assignedToMemberId: 'member_sales'
});

// Unassign a thread
await client.patch(`/threads/${threadId}`, {
  assignedToMemberId: null
});

Filter by Assignee

Get threads assigned to specific members:
// Get threads assigned to a specific member
const { data } = await client.get('/threads', {
  params: {
    'filter[assignedToMemberIds][0]': 'member_sales'
  }
});

// Get unassigned threads
const { data: unassigned } = await client.get('/threads', {
  params: {
    'filter[noAssignee]': true
  }
});

Common Patterns

Round-Robin Assignment

Distribute new threads evenly across team members:
async function getTeamMembers(): Promise<string[]> {
  const { data } = await client.get('/members');
  // Exclude owner from assignment pool
  return data.members
    .filter((m: any) => m.role !== 'owner')
    .map((m: any) => m.id);
}

class RoundRobinAssigner {
  private members: string[] = [];
  private currentIndex = 0;

  async initialize() {
    this.members = await getTeamMembers();
  }

  next(): string {
    const memberId = this.members[this.currentIndex];
    this.currentIndex = (this.currentIndex + 1) % this.members.length;
    return memberId;
  }
}

// Usage
const assigner = new RoundRobinAssigner();
await assigner.initialize();

async function assignNewThread(threadId: string) {
  const memberId = assigner.next();
  await client.patch(`/threads/${threadId}`, {
    assignedToMemberId: memberId
  });
  console.log(`Assigned thread to ${memberId}`);
}

Workload-Based Assignment

Assign to the team member with the fewest open threads:
async function getMemberWorkloads(): Promise<Map<string, number>> {
  const { data: membersData } = await client.get('/members');
  const workloads = new Map<string, number>();

  for (const member of membersData.members) {
    if (member.role === 'owner') continue;

    const { data: threadsData } = await client.get('/threads', {
      params: {
        'filter[assignedToMemberIds][0]': member.id,
        inboxView: 'default',
        limit: 1  // We just need the count
      }
    });

    workloads.set(member.id, threadsData.threads.length);
  }

  return workloads;
}

async function assignToLeastBusy(threadId: string) {
  const workloads = await getMemberWorkloads();

  // Find member with lowest workload
  let minMember = '';
  let minCount = Infinity;

  for (const [memberId, count] of workloads) {
    if (count < minCount) {
      minCount = count;
      minMember = memberId;
    }
  }

  if (minMember) {
    await client.patch(`/threads/${threadId}`, {
      assignedToMemberId: minMember
    });
    console.log(`Assigned to ${minMember} (${minCount} threads)`);
  }
}

Role-Based Routing

Route threads based on content to appropriate team members:
interface RoutingConfig {
  memberId: string;
  keywords: string[];
}

const routingRules: RoutingConfig[] = [
  {
    memberId: 'member_sales',
    keywords: ['pricing', 'demo', 'quote', 'buy', 'purchase']
  },
  {
    memberId: 'member_support',
    keywords: ['help', 'issue', 'problem', 'bug', 'error', 'not working']
  },
  {
    memberId: 'member_partnerships',
    keywords: ['partner', 'integration', 'affiliate', 'collaborate']
  }
];

async function routeByContent(threadId: string, messageText: string) {
  const text = messageText.toLowerCase();

  for (const rule of routingRules) {
    if (rule.keywords.some(k => text.includes(k))) {
      await client.patch(`/threads/${threadId}`, {
        assignedToMemberId: rule.memberId
      });
      console.log(`Routed to ${rule.memberId} based on keywords`);
      return rule.memberId;
    }
  }

  // Default: round-robin if no keywords match
  return null;
}

Member Performance Metrics

Track team performance:
interface MemberMetrics {
  memberId: string;
  name: string;
  openThreads: number;
  closedToday: number;
  avgResponseTime?: number;
}

async function getTeamMetrics(): Promise<MemberMetrics[]> {
  const { data: membersData } = await client.get('/members');
  const metrics: MemberMetrics[] = [];

  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);

  for (const member of membersData.members) {
    // Count open threads
    const { data: openData } = await client.get('/threads', {
      params: {
        'filter[assignedToMemberIds][0]': member.id,
        inboxView: 'default'
      }
    });

    // Count archived today (approximation)
    const { data: archivedData } = await client.get('/threads', {
      params: {
        'filter[assignedToMemberIds][0]': member.id,
        inboxView: 'archived'
      }
    });

    metrics.push({
      memberId: member.id,
      name: member.name,
      openThreads: openData.threads.length,
      closedToday: archivedData.threads.filter(
        (t: any) => new Date(t.updatedAt) >= todayStart
      ).length
    });

    await new Promise(r => setTimeout(r, 100));  // Rate limit
  }

  return metrics;
}

// Generate report
const metrics = await getTeamMetrics();
console.log('Team Performance:');
metrics.forEach(m => {
  console.log(`  ${m.name}: ${m.openThreads} open, ${m.closedToday} closed today`);
});

Best Practices

Use workload-based or round-robin assignment to prevent bottlenecks.
Each thread should have a clear owner. Avoid leaving threads unassigned for long.
Assign admin roles sparingly. Most team members only need member access.
Regularly check team metrics to identify overloaded members.

Next Steps