Skip to main content

Overview

Threads are the core of conversation management in Inbox. This guide covers creating, retrieving, updating, and organizing threads.
See the Core Concepts guide to understand the thread data model first.

Listing Threads

Retrieve threads with filtering and pagination:
const { data } = await client.get('/threads', {
  params: {
    limit: 50,
    inboxView: 'default',
    accountLinkId: 'acc_abc123'
  }
});

console.log(`Found ${data.threads.length} threads`);
data.threads.forEach(thread => {
  console.log(`Thread ${thread.id} with ${thread.prospect.platformUsername}`);
});
Response structure:
{
  "threads": [
    {
      "id": "thread_abc123",
      "platformId": "12345-67890",
      "accountLinkId": "acc_abc",
      "prospectId": "prospect_xyz",
      "done": false,
      "assignedToMemberId": null,
      "createdAt": "2025-11-20T10:00:00.000Z",
      "updatedAt": "2025-11-23T15:30:00.000Z",
      "lastMessageAt": "2025-11-23T15:30:00.000Z",
      "prospect": {
        "id": "prospect_xyz",
        "platformId": "987654321",
        "platformUsername": "johndoe",
        "name": "John Doe"
      }
    }
  ],
  "nextCursor": {
    "id": "thread_xyz789",
    "timestamp": "2025-11-23T15:30:00.000Z"
  }
}

Filtering Threads

The API supports sophisticated filtering to find exactly the threads you need.

By Inbox View

// Active conversations
const active = await client.get('/threads', {
  params: { inboxView: 'default' }
});

// Awaiting replies
const noReply = await client.get('/threads', {
  params: { inboxView: 'no-reply' }
});

// New message requests
const requests = await client.get('/threads', {
  params: { inboxView: 'requests' }
});

// Archived (done)
const archived = await client.get('/threads', {
  params: { inboxView: 'archived' }
});
Filter threads for a specific connected account:
const { data } = await client.get('/threads', {
  params: {
    accountLinkId: 'acc_abc123'
  }
});

By Tags

Find threads with prospects that have specific tags:
// Prospects with ANY of these tags
const { data } = await client.get('/threads', {
  params: {
    'filter[tagIds][0]': 'tag_hot_lead',
    'filter[tagIds][1]': 'tag_customer'
  }
});

// Prospects with NO tags
const { data: untagged } = await client.get('/threads', {
  params: {
    'filter[noTags]': true
  }
});

By Status

// Threads with prospects in "qualified" status
const { data } = await client.get('/threads', {
  params: {
    'filter[statusIds][0]': 'status_qualified'
  }
});

// Prospects with no status
const { data: noStatus } = await client.get('/threads', {
  params: {
    'filter[noStatus]': true
  }
});

By Assignee

// Assigned to specific member
const { data } = await client.get('/threads', {
  params: {
    'filter[assignedToMemberIds][0]': 'member_abc123'
  }
});

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

Combined Filters

Combine multiple filters for precise results:
const { data } = await client.get('/threads', {
  params: {
    inboxView: 'default',
    accountLinkId: 'acc_abc',
    'filter[statusIds][0]': 'status_qualified',
    'filter[assignedToMemberIds][0]': 'member_xyz',
    limit: 20
  }
});

// Returns: Active threads from acc_abc, with qualified prospects, assigned to member_xyz

Pagination

Use cursor-based pagination to retrieve all threads:
async function getAllThreads() {
  const allThreads = [];
  let cursor = undefined;

  do {
    const { data } = await client.get('/threads', {
      params: {
        cursor: cursor ? {
          id: cursor.id,
          timestamp: cursor.timestamp
        } : undefined,
        limit: 50
      }
    });

    allThreads.push(...data.threads);
    cursor = data.nextCursor;

  } while (cursor);

  return allThreads;
}
The cursor object needs special encoding in URLs. See the Query Parameters guide for details.

Looking Up a Thread

Find an existing thread between an account and a prospect:
const { data } = await client.get('/threads/lookup', {
  params: {
    prospectPlatformId: '987654321',  // X user ID
    accountLinkId: 'acc_abc123'
  }
});

if (data.thread) {
  console.log('Found thread:', data.thread.id);
} else {
  console.log('No thread exists yet');
}
You can also lookup by prospectId (Inbox ID) instead of prospectPlatformId.

Creating a Thread

Create a new conversation thread:
const { data } = await client.post('/threads', {
  prospectPlatformId: '987654321',
  accountLinkId: 'acc_abc123'
});

console.log('Created thread:', data.thread.id);
When to create threads:
  • When starting a new conversation
  • After lookup returns no existing thread
  • For proactive outreach
Creating a thread doesn’t send a message. Use the messages endpoint to actually send a DM.

Getting Thread Details

Retrieve full details for a specific thread:
const { data } = await client.get(`/threads/${threadId}`);

console.log('Thread:', data.thread);
console.log('Prospect:', data.thread.prospect);
console.log('Last message:', data.thread.lastMessageAt);

Updating Threads

Modify thread properties like assignment and done status:
// Assign thread to a team member
await client.patch(`/threads/${threadId}`, {
  assignedToMemberId: 'member_abc123'
});

// Mark thread as done (archives it)
await client.patch(`/threads/${threadId}`, {
  done: true
});

// Unassign and reopen
await client.patch(`/threads/${threadId}`, {
  assignedToMemberId: null,
  done: false
});

Deleting Threads

Permanently delete a thread and its messages:
await client.delete(`/threads/${threadId}`);
console.log('Thread deleted');
Deletion is permanent and cannot be undone. Consider using done: true to archive instead.

Common Workflows

Get Threads Needing Follow-Up

// Threads where we sent last message but no reply
const { data } = await client.get('/threads', {
  params: {
    inboxView: 'no-reply',
    limit: 50
  }
});

console.log(`${data.threads.length} threads need follow-up`);

Assign Incoming Requests Round-Robin

const members = ['member_1', 'member_2', 'member_3'];
let currentIndex = 0;

async function assignNewRequest(threadId) {
  const assigneeId = members[currentIndex];

  await client.patch(`/threads/${threadId}`, {
    assignedToMemberId: assigneeId
  });

  currentIndex = (currentIndex + 1) % members.length;
  console.log(`Assigned to ${assigneeId}`);
}

Archive Completed Conversations

async function archiveThread(threadId) {
  // Mark as done and unassign
  await client.patch(`/threads/${threadId}`, {
    done: true,
    assignedToMemberId: null
  });

  console.log('Thread archived');
}

Find High-Value Prospects

const { data } = await client.get('/threads', {
  params: {
    'filter[tagIds][0]': 'tag_enterprise',
    'filter[statusIds][0]': 'status_qualified',
    inboxView: 'default'
  }
});

console.log(`${data.threads.length} enterprise qualified leads`);

Best Practices

Always use /threads/lookup before creating to avoid duplicates:
// Lookup first
const { data: lookup } = await client.get('/threads/lookup', {
  params: { prospectPlatformId, accountLinkId }
});

// Create only if needed
const thread = lookup.thread ?? (
  await client.post('/threads', {
    prospectPlatformId,
    accountLinkId
  })
).data.thread;
  • Default: 50 threads per page
  • For fast scans: 100 threads
  • For detailed processing: 20-30 threads
const { data } = await client.get('/threads', {
  params: { limit: 50 }
});
Be specific with filters to reduce API calls and get exactly what you need.
Use done: true to archive threads unless you need permanent deletion. Archived threads can be restored.

Next Steps