Skip to main content

Overview

The Inbox API manages DM conversations across messaging platforms. Understanding these core entities will help you build effective integrations.
Multi-Platform Future: The API currently supports X (Twitter) DMs. Instagram and LinkedIn are coming in Q2 2026. The data model is platform-agnostic — see Supported platforms.

Key entities

Threads

A thread represents a DM conversation between one of your account links and a prospect.
{
  "id": "l44e15irdq4db30i77cgphhx",
  "platform": "twitter",
  "platformId": "1566123362161725440:1876543210987654321",
  "done": false,
  "assigneeId": null,
  "lastMessageTimestamp": "2025-01-15T15:30:00.000Z",
  "computedSortTimestamp": "2025-01-15T15:30:00.000Z",
  "createdAt": "2025-01-10T10:00:00.000Z",
  "status": "active",
  "variant": "xChat",
  "accountLinkId": "df6jbw4h36qm5d9iu2sgn7kx",
  "isSyncing": false,
  "isRequest": false,
  "typingIndicatorsEnabled": null,
  "lastMessage": {
    "id": "p8rvk2m5j0xn4wq7ybftcael",
    "content": "Sounds great, let's schedule a call!",
    "authorId": "hzcai5t59nn9vsck3rbuepyg",
    "createdAt": "2025-01-15T15:30:00.000Z",
    "userId": null,
    "campaignId": null
  },
  "prospect": { "..." }
}
Key fields:
FieldDescription
idInbox thread ID — use this for all API operations
platformThe platform this thread belongs to (currently "twitter")
platformIdThe X conversation ID (formatted as smallerUserId:largerUserId)
doneWhether the thread is archived
assigneeIdTeam member assigned to this thread (null if unassigned)
status"active" (visible in inbox) or "idle" (hidden, e.g. Quick Peek previews)
variant"unencrypted" (legacy DMs) or "xChat" (encrypted DMs)
accountLinkIdWhich connected X account this thread belongs to
isRequestWhether this is a message request from an unknown prospect
lastMessagePreview of the most recent message
prospectThe full prospect object (with context) embedded in the thread

Messages

Messages are individual DMs sent or received in a thread.
{
  "id": "p8rvk2m5j0xn4wq7ybftcael",
  "platform": "twitter",
  "platformId": "1876543210987654322",
  "threadId": "l44e15irdq4db30i77cgphhx",
  "teamId": "hzcai5t59nn9vsck3rbuepyg",
  "authorId": "df6jbw4h36qm5d9iu2sgn7kx",
  "userId": "r3km7xj9wq5p2bvnhfdteoly",
  "campaignId": null,
  "content": "Thanks for reaching out! I'd love to learn more about your project.",
  "origin": "api",
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": null,
  "isEdited": false,
  "entities": null,
  "attachment": null,
  "reactions": [],
  "replyData": null,
  "forwardData": null
}
Key fields:
FieldDescription
contentThe message text
authorIdEither the accountLinkId or the prospect’s externalId — identifies the sender within Inbox (not a platform ID)
userIdInbox user ID if sent from Inbox by a team member. null if the message is from the prospect, was sent from the X client, or was otherwise sent outside of Inbox. A null value does not necessarily mean the message is from the prospect — it just means it wasn’t sent from Inbox.
originWhere the message originated: "external" (pulled from X), "internal" (sent from the Inbox UI or a campaign), "api" (sent via the API), or null
campaignIdNon-null if the message was sent by a campaign (these have origin: "internal")
isEditedWhether the message has been edited
There is no direction field on messages. Check authorId against your account link IDs to determine the sender. See Working with messages — Determining message direction for details.

Prospects

A prospect represents an external user on a messaging platform.
{
  "platform": "twitter",
  "platformId": "1876543210987654321",
  "externalId": "hzcai5t59nn9vsck3rbuepyg",
  "documentId": 858224163,
  "displayName": "Jordan Rivera",
  "username": "jordanrivera",
  "handle": "@jordanrivera",
  "image": "https://pbs.twimg.com/profile_images/.../photo.jpg",
  "imageNormalized": "https://pbs.twimg.com/profile_images/.../photo.jpg",
  "bio": "Head of Growth at TechCo. Building the future of SaaS.",
  "location": "San Francisco, CA",
  "profileUrl": "https://x.com/jordanrivera",
  "websiteUrl": "https://techco.com",
  "websiteDomain": "techco.com",
  "verified": "none",
  "profileType": "personal",
  "isProtected": false,
  "followerCount": 12400,
  "followingCount": 892,
  "postCount": 3420,
  "engagementCount": 8100,
  "listedCount": 18,
  "platformCreatedAt": "2019-03-15T08:00:00.000Z",
  "firstSeenAt": "2024-07-09T14:08:49.000Z",
  "lastUpdatedAt": "2025-01-15T10:30:00.000Z",
  "lastEnrichedAt": "2025-01-15T10:30:00.000Z",
  "lastActiveAt": "2025-01-14T12:00:00.000Z",
  "source": "cached",
  "isFresh": true,
  "isStale": false,
  "confidence": 0.85,
  "platformData": {
    "isVerifiedBlue": false,
    "isVerifiedGold": false,
    "isVerifiedGray": false,
    "professionalCategory": null,
    "urlEntities": [],
    "bannerUrl": "https://pbs.twimg.com/profile_banners/...",
    "tweetsCount": 3420,
    "favoritesCount": 8100,
    "rawData": null
  }
}
Key differences from what you might expect:
FieldNot thisIt’s this
Display namenamedisplayName
Biodescriptionbio
AvatarprofileImageUrlimage (or imageNormalized for highest quality)
FollowersfollowersCountfollowerCount (singular)
Verifiedtrue / false"verified" (blue check), "business" (gold), "government" (gray), or "none"
Profile type"personal", "business", or "government"
Source"cached", "indexed", "live", or "merged"
Prospects are shared across all account links in your team. When retrieved as part of a thread or via the prospects endpoints, they include a context object with your team’s custom data.

Prospect context

When you retrieve a prospect (via threads or the prospects API), the response includes a context object:
{
  "context": {
    "tags": ["t8rvk2m5j0xn4wq7ybftcael"],
    "statusId": "s3km7xj9wq5p2bvnhfdteoly",
    "valuation": 50000,
    "notes": "Decision maker at TechCo. Follow up next week."
  }
}
FieldTypeDescription
tagsstring[]Array of tag IDs applied to this prospect
statusIdstring | nullPipeline status ID
valuationnumber | nullDeal value
notesstring | nullFree-text notes
Update context with PATCH /prospects/{prospectId}/context:
await client.patch(`/prospects/${prospectId}/context`, {
  notes: "Interested in Enterprise plan",
  valuation: 50000,
  statusId: "s3km7xj9wq5p2bvnhfdteoly",
  addTags: ["t8rvk2m5j0xn4wq7ybftcael"],
  removeTags: ["old_tag_id_here"],
});
Tags are modified with addTags and removeTags — there is no tagIds field. See Tags & statuses for details.
An account link is an X account connected to your team.
{
  "id": "df6jbw4h36qm5d9iu2sgn7kx",
  "platform": "twitter",
  "platformId": "1566123362161725440",
  "name": "Acme Corp",
  "username": "acmecorp",
  "image": "https://pbs.twimg.com/profile_images/.../photo.jpg",
  "createdAt": "2024-06-01T12:00:00.000Z",
  "status": "active",
  "syncedAt": "2025-01-15T10:30:00.000Z"
}
Use account links to:
  • Send messages from specific accounts
  • Filter threads by account
  • Manage multi-account workflows

Tags

Tags are flexible labels you apply to prospects for organization.
{
  "id": "t8rvk2m5j0xn4wq7ybftcael",
  "name": "Hot Lead",
  "color": "#ef4444"
}
A prospect can have multiple tags. Colors accept either a predefined name (e.g., "red", "blue", "green") or a hex code (e.g., "#ef4444"). Use GET /colors to see all predefined options.

Statuses

Statuses represent pipeline stages for a prospect.
{
  "id": "s3km7xj9wq5p2bvnhfdteoly",
  "name": "Qualified",
  "color": "#f59e0b",
  "createdAt": "2024-06-01T12:00:00.000Z"
}
A prospect can have only one status at a time. Use statuses for linear progression (e.g., New → Qualified → Won). Use tags for non-linear attributes.

Members

Members are users on your team.
{
  "id": "r3km7xj9wq5p2bvnhfdteoly",
  "userId": "usr4hn8xj2wq5p1bvnhfdteo",
  "name": "Alex Chen",
  "email": "alex@acmecorp.com",
  "image": "https://lh3.googleusercontent.com/...",
  "role": "admin",
  "createdAt": "2024-06-01T12:00:00.000Z",
  "updatedAt": null
}
Roles are "owner", "admin", or "user". Member IDs are used for assigning threads and filtering by assignee.

The dual ID system

Inbox uses two types of IDs for platform-related objects:

Inbox ID (id)

e.g. l44e15irdq4db30i77cgphhxThe internal Inbox ID (CUID2 format). Use this for all API operations.

Platform ID (platformId)

e.g. 1876543210987654321The original ID from X. Use this for lookups and constructing X URLs.
// Use Inbox IDs for API operations
await client.get(`/threads/${thread.id}/messages`);

// Use Platform IDs for lookups
await client.get("/threads/lookup", {
  params: {
    externalPlatformId: "1876543210987654321",
    accountLinkId: account.id,
  },
});

Helpful X URLs

You can use platform IDs to construct direct links to X:
Link typeURL pattern
User profile (by ID)https://x.com/i/user/{platformId}
User profile (by username)https://x.com/{username}
Tweet/Posthttps://x.com/anyusername/status/{tweetId}
DM conversationhttps://x.com/i/chat/{conversationPlatformId}
The user-by-ID link (/i/user/{platformId}) is the most reliable since usernames can change. For tweet URLs, X ignores the username — only the tweet ID matters.

Thread status vs Inbox views

The thread status field ("active" or "idle") controls whether the thread is visible in the inbox. Active threads appear in inbox views; idle threads are hidden (e.g., threads created via Quick Peek that haven’t had a real conversation yet). This is separate from inbox views, which are filtered using the inbox query parameter:
inbox valueDescription
"default"Active conversations needing attention
"no-reply"The prospect sent the last message and is awaiting your reply
"requests"New message requests from unknown prospects
"archived"Threads marked as done (done: true)
Filter by inbox view:
// Active conversations
const { data } = await client.get("/threads", {
  params: { inbox: "default" },
});

// Threads where the prospect is awaiting your reply
const { data: noReply } = await client.get("/threads", {
  params: { inbox: "no-reply" },
});

// Archived threads
const { data: archived } = await client.get("/threads", {
  params: { inbox: "archived" },
});

Response shapes

Different endpoints return data in different shapes. Here’s what to expect:
PatternEndpointsShape
Single objectGET /threads/{id}, POST /threads, GET /threads/lookupReturns the object directly
ArrayGET /tags, GET /statuses, GET /account-links, GET /membersReturns an array directly
PaginatedGET /threads, GET /threads/{id}/messagesReturns { threads, nextCursor } or { messages, nextCursor }
// Direct object — no wrapper
const { data: thread } = await client.get(`/threads/${threadId}`);
console.log(thread.id);

// Direct array — no wrapper
const { data: tags } = await client.get("/tags");
console.log(tags.length);

// Paginated — has wrapper
const { data } = await client.get("/threads");
console.log(data.threads.length, data.nextCursor);

Pagination

List endpoints use cursor-based pagination:
const allThreads = [];
let cursorId: string | undefined;
let cursorTimestamp: string | undefined;

do {
  const { data } = await client.get("/threads", {
    params: {
      limit: 100,
      ...(cursorId && { cursorId, cursorTimestamp }),
    },
  });

  allThreads.push(...data.threads);

  cursorId = data.nextCursor?.id;
  cursorTimestamp = data.nextCursor?.timestamp;
} while (cursorId);
Use the flat cursorId and cursorTimestamp parameters instead of bracket notation (cursor[id]). The bracket notation form is deprecated.

Entity relationships

Team
├── Members (team users)
├── Account Links (connected X accounts)
├── Tags (labels for prospects)
├── Statuses (pipeline stages)
└── Prospects (external X users)
    ├── Context (tags, status, notes, valuation)
    └── Threads (one per account link + prospect pair)
        └── Messages
  • A prospect can have multiple tags but only one status
  • A thread belongs to exactly one account link and one prospect
  • A prospect can have threads across multiple account links

Next steps