Skip to main content
GET
/
api
/
conversations
List campaign conversations
curl --request GET \
  --url https://app.puffle.ai/api/conversations \
  --header 'Authorization: Bearer <token>'
{
  "conversations": [
    {
      "id": "f11e8a2c-1234-4567-89ab-cdef01234567",
      "user_id": "11111111-1111-1111-1111-111111111111",
      "account_id": "aaaa1111-1111-1111-1111-111111111111",
      "channel": "linkedin",
      "status": "active",
      "external_id": "unipile_chat_abc123",
      "participant_name": "Jane Doe",
      "participant_email": null,
      "participant_linkedin_url": "https://linkedin.com/in/jane",
      "last_message_at": "2026-04-22T09:12:00Z",
      "unread_count": 1,
      "created_at": "2026-04-20T15:00:00Z",
      "updated_at": "2026-04-22T09:12:00Z",
      "last_message_snippet": "Thanks for reaching out — happy to chat next week.",
      "last_message_direction": "inbound",
      "participant_company": "Acme Inc"
    },
    {
      "id": "f22e8a2c-1234-4567-89ab-cdef01234567",
      "user_id": "11111111-1111-1111-1111-111111111111",
      "account_id": "aaaa2222-1111-1111-1111-111111111111",
      "channel": "email",
      "status": "active",
      "external_id": "am_thread_xyz",
      "participant_name": "John Smith",
      "participant_email": "[email protected]",
      "participant_linkedin_url": null,
      "last_message_at": "2026-04-21T17:30:00Z",
      "unread_count": 0,
      "created_at": "2026-04-18T12:00:00Z",
      "updated_at": "2026-04-21T17:30:00Z",
      "last_message_snippet": "Sounds good — sending a calendar invite.",
      "last_message_direction": "outbound",
      "participant_company": "Beta Co"
    }
  ],
  "total": 2,
  "page": 1,
  "limit": 50
}

Overview

Returns the caller’s inbox threads, sorted by last_message_at DESC. Each conversation represents a single LinkedIn or email thread between one of your sender accounts and one prospect — they are created by inbound webhooks (Unipile message_received, AgentMail message.received) when a prospect replies to a campaign, or by the first outbound reply sent through sendConversationMessage. Each item is enriched server-side with:
  • last_message_snippet — first 100 characters of the most recent message
  • last_message_directioninbound or outbound, useful for unread-badge logic client-side
  • participant_company — looked up from campaign_prospects via the participant’s LinkedIn URL or email
Use the filters to narrow down:
QueryEffect
?status=active / ?status=archivedArchive state
?channel=linkedin / ?channel=emailChannel
?campaign_id={uuid}Only threads with messages tagged to that campaign
?page=N&limit=MPagination — limit defaults to 50, max 100

AI agent notes

Inbox entries are webhook-driven. A conversation only exists once a webhook has fired — there is no “create conversation” endpoint. Preconditions to see a thread show up:
  • LinkedIn: campaign must be sending through a connected Unipile account, and the prospect must reply or accept a connection request. The Unipile webhook creates the conversations row.
  • Email: campaign must be sending through a connected AgentMail inbox, and the prospect must reply. The AgentMail webhook creates the row.
No side effects. This endpoint is a pure read — safe to poll. Respect the 100 req/min global rate limit.Polling strategy. Combine last_message_at with a client-stored “last seen” timestamp per conversation id to decide what’s unread. Server-side unread_count is authoritative but gets reset to 0 as soon as anyone calls getConversation, so relying on it alone can race across clients.Chaining. To act on a thread the agent should:
  1. listConversations to find the conversation id.
  2. getConversation (or listConversationMessages) to read the history.
  3. sendConversationMessage to reply, or updateConversation to archive.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Query Parameters

status
enum<string>

Filter by status. Default is to return all statuses.

Available options:
active,
archived
channel
enum<string>

Filter by channel. Values outside linkedin|email are silently ignored.

Available options:
linkedin,
email
campaign_id
string<uuid>

Return only conversations whose messages include at least one row tagged with this campaign id.

Pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
page
integer

1-indexed page number. Defaults to 1.

Required range: 0 < x <= 9007199254740991
limit
integer

Page size. Defaults to 50, max 100.

Required range: 0 < x <= 100

Response

Paginated list of conversations. Empty array when the user has no matching threads.

conversations
object[]
required
total
integer
required

Total matching rows across all pages (ignores page/limit). Use to drive pagination controls.

Required range: 0 <= x <= 9007199254740991
page
integer
required
Required range: 0 < x <= 9007199254740991
limit
integer
required
Required range: 0 < x <= 9007199254740991