Inbox
List LinkedIn conversations, read message history, reply with text, attachments or voice notes, and react to replies programmatically.
The inbox is every LinkedIn conversation your connected sender accounts are part of, in one place. Campaign steps, direct actions and manual messages all land in the same threads, so the inbox API is where you build reply handling: routing hot leads to a CRM, powering a custom unibox, or auto-responding to replies.
The chat and message model
A chat is one conversation thread between a sender account and a LinkedIn member. A message is one entry inside a chat.
Key chat fields:
| Field | Type | Meaning |
|---|---|---|
id | string | The chat id used in every /inbox/chats/{id} call. |
accountId | string, null | The sender account on your side of the thread. |
attendee | object, null | Snapshot of the LinkedIn person on the other side. |
leadId | string, null | The workspace lead this person maps to, when tracked. |
lastMessage | object, null | The most recent message in the thread. |
lastMessageAt | date-time, null | When the thread last had activity. |
lastReplyAt | date-time, null | When the lead last messaged this thread. Null until they answer, and never cleared once set: your own replies do not reset it. |
unreadCount | integer | Unread messages in the thread. |
favorite, archived | boolean | Inbox flags. |
Key message fields:
| Field | Type | Meaning |
|---|---|---|
id, chatId | string | Identity and parent thread. |
text | string | The message body. |
isFromMe | boolean | true for messages your sender account sent. |
isEvent | boolean | true for system events rather than regular messages. |
attachments | array | Media sent with the message (image, video, audio, file), currently populated for outgoing messages. Voice notes carry voiceNote: true and durationMs. |
at | date-time | When the message was sent. |
List and filter chats
listInboxChats returns chats ordered by most recent activity, with standard limit (1 to 100, default 50) and offset paging.
The replied query parameter filters on whether the lead has answered:
replied=true: only threads where the lead has sent at least one message. These are ordered by the lead's most recent reply instead of general activity, so the freshest replies come first. Threads stored before reply tracking have no recorded reply time and sort last.replied=false: only threads still awaiting a first reply.- Omit it for all chats.
curl "https://app.swarmhit.com/api/v1/inbox/chats?replied=true&limit=25" \
-H "Authorization: Bearer swh_live_..."This is the polling-friendly view of "who answered me": page through replied=true and compare each chat's lastReplyAt against your own high-water mark. For push instead of polling, see reacting to replies below.
Read a conversation
Fetch a single thread with getInboxChat, and its history with listChatMessages:
curl "https://app.swarmhit.com/api/v1/inbox/chats/CHAT_ID/messages?limit=50" \
-H "Authorization: Bearer swh_live_..."Use isFromMe to render the two sides of the conversation and isEvent to filter out system entries.
If you want a lead-centric view rather than a thread-centric one, getLeadActivity merges campaign step transitions and inbox messages into one newest-first timeline for a lead (capped at 200 events).
Mark a chat read
markChatRead clears the unread count for the chat, and for every sibling thread with the same person, both locally and on LinkedIn:
curl -X POST "https://app.swarmhit.com/api/v1/inbox/chats/CHAT_ID/read" \
-H "Authorization: Bearer swh_live_..."Reply in a chat
sendChatMessage sends a message into an existing thread:
curl -X POST "https://app.swarmhit.com/api/v1/inbox/chats/CHAT_ID/messages" \
-H "Authorization: Bearer swh_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4f2c9a1e-7d31-4b8a-9f0e-2d6c8b5a1e3f" \
-d '{"text": "Thanks for connecting! Happy to share more."}'Always send an Idempotency-Key header (any unique string, for example a UUID). Reusing the key replays the first response instead of sending again, so a network retry cannot double-message a lead. A 409 with code: idempotency_in_progress means the original request is still in flight (retry shortly); code: idempotency_failed means the original errored after it may have taken effect (retry with a new key).
The response includes messageId and, when the chat's counterpart is a campaign lead, a campaignLead object with its id and status so your app can offer to pause or stop the sequence.
This endpoint is for existing threads. To start a brand new conversation with a connection, use the direct action sendDirectMessage instead. See Direct actions.
Attachments and voice notes
text becomes optional when you provide an attachment or a voice message. Attachments are fetched server-side from a public url (photo, video or document, max 15 MB each):
curl -X POST "https://app.swarmhit.com/api/v1/inbox/chats/CHAT_ID/messages" \
-H "Authorization: Bearer swh_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9b7d3e5f-1a24-4c6d-8e90-3f5a7c9b1d2e" \
-d '{
"text": "Here is the one-pager I mentioned.",
"attachments": [
{ "url": "https://example.com/files/one-pager.pdf", "name": "one-pager.pdf" }
]
}'voiceMessage sends a LinkedIn voice note: the audio at its url is fetched and transcoded to LinkedIn's format server-side (max about 1 minute), with an optional durationMs.
Halting the sequence on send
If the chat's lead is running in a campaign, replying manually via the API does not interrupt the sequence by itself. Use sequenceAction to halt it on send:
"pause": freezes the sequence, keeping step progress and timers, so it can be resumed later."stop": terminal. The executor never contacts this lead again in that campaign.- Omit it (or send
null) to leave the sequence running.
curl -X POST "https://app.swarmhit.com/api/v1/inbox/chats/CHAT_ID/messages" \
-H "Authorization: Bearer swh_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f" \
-d '{"text": "Great, let me send over some times.", "sequenceAction": "stop"}'You can also pause, resume or stop the lead independently of sending, via pauseLeadInCampaign, resumeLeadInCampaign and stopLeadInCampaign. Note that a manual message sent from the inbox inside the Swarmhit app pauses the lead automatically; the API gives you the explicit sequenceAction control instead.
React to replies programmatically
Polling replied=true works, but the push path is webhooks. Subscribe an endpoint to message.received and Swarmhit calls you the moment a lead replies:
curl -X POST "https://app.swarmhit.com/api/v1/webhooks" \
-H "Authorization: Bearer swh_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/swarmhit",
"events": ["message.received"],
"classifyReplies": true
}'The message.received payload carries everything you need to act without extra lookups:
data.source:campaignwhen the reply is on a campaign lead,directwhen it is on a lead you actioned via the API.data.accountId: the sender account the reply arrived on.data.campaignIdanddata.campaignLeadId: set for campaign replies, null for direct ones.data.leadIdanddata.chatId: the lead and the thread, so you can fetch history and reply.data.message: the replytextand its timestamp.data.from: the replier'sfirstName,lastName,publicIdentifierandproviderId.
A typical handler: fetch the thread with data.chatId via listChatMessages for context, send a reply with sendChatMessage, then markChatRead. The outbound mirror event, message.sent, fires for outgoing messages on tracked-lead chats, so you can keep both sides of the conversation in sync in your own system.
Deliveries are at-least-once: deduplicate on the X-Swarmhit-Delivery header and verify the X-Swarmhit-Signature HMAC. The Webhooks guide covers both.
Reply classification
With classifyReplies enabled on a webhook endpoint, every message.received delivery to it carries a classification.interest verdict: interested, not_interested or neutral, or null when no classification was produced (treat null as unknown, never as neutral). Classification consumes workspace credits. The full contract, vocabulary and example payload live in the Webhooks guide.
To persist the verdict on the campaign membership, call setLeadInterest with interested or not_interested (the stored interest field has no neutral value; its companion interestBy records whether it was set by manual action or ai). Campaigns can also do this themselves when aiInterestEnabled is on.
Reference
List inbox chats
Page through conversations, with the replied filter.
List messages in a chat
Read a thread's history.
Send a message in a chat
Reply with text, attachments or a voice note.
Mark a chat read
Clear unread counts locally and on LinkedIn.
Webhooks guide
Subscribe to message.received and verify deliveries.