Social Inbox API: DMs, Comments, Mentions
Read and reply to your Instagram and Facebook DMs, comments, and mentions, LinkedIn company-page comments and mentions, TikTok and YouTube video comments, and Threads replies and mentions, all from the API. Conversations are served from OmniSocials, so you read at your own pace without hitting each platform's rate limits, and replies go out through your connected accounts.
There is also a work queue, GET /inbox/next, that hands you the next thing that needs an answer together with its conversation and post, and comment moderation (hide, delete) on the platforms that allow it. See Working through the inbox and Hiding and deleting comments.
X (Twitter) DMs are also available as a per-workspace opt-in, see X direct messages. Threads support is currently rolling out, see Threads replies and mentions.
Enabling inbox access
The inbox scopes are opt-in. When you create an API key, turn on Social Inbox access to grant both inbox scopes. Existing keys are not given these scopes automatically.
| Scope | Grants |
|---|---|
inbox:read | List conversations, read messages, and pull the next unanswered item |
inbox:write | Mark conversations read, send replies, hide and delete comments |
When you connect through OAuth or the MCP server, inbox access is governed by the access_social_inbox workspace permission instead of scopes. Company owners and admins have it by default.
Listing conversations
Returns one entry per conversation, newest activity first.
Code
Response:
Code
Query parameters (all optional):
| Parameter | Description |
|---|---|
platform | instagram, facebook, linkedin, tiktok, youtube, x, or threads |
type | dm, comment, or mention |
unread | true returns only conversations with unread incoming messages |
unanswered | true returns only conversations that still need an answer: the person's latest DM has no reply after it (Instagram and Facebook DMs within the 24-hour messaging window only), or a comment or mention has not been replied to and is not hidden. Replies sent from the native apps count as answers. Read state is ignored here; use GET /inbox/next for a work queue. See What counts as unanswered |
limit | Conversations per page, 1 to 100 (default 25) |
cursor | The next_cursor from a previous response |
post is present for comment and mention conversations (the post the interaction is on) and null for DMs. See Post context on comments.
Cursor pagination
The inbox list endpoints use cursor pagination, not offset. Every response carries a pagination object:
Code
To fetch the next page, pass next_cursor back as the cursor query parameter. When has_more is false, next_cursor is null and you have reached the end.
Reading a conversation
Returns the full message history, oldest first. Also cursor-paginated (default limit 50).
Code
Response:
Code
direction is incoming for messages from the other person and outgoing for your replies. Replies sent from the platform's own app show up as outgoing too, see Replies sent from the native apps.
permalink links to the reply or mentioning post on the platform when one is known, null otherwise. hidden is set on comments and mentions: true when the comment is hidden on the platform (see Hiding and deleting comments), false when it is visible. It is null on DMs.
Media on a message
attachment is set on messages that carry media, and null otherwise. It carries url and type (image, video, audio, or file):
Code
This applies to inbound Instagram and Facebook DM photos, videos, voice messages, and Instagram story mentions, and to any reply you sent with attachment_url. Instagram/Facebook's own attachment URL is short-lived (it typically stops resolving within hours); we download it once when the message arrives and re-host it on our own CDN, so attachment.url keeps working indefinitely.
type reflects the fetched bytes, not the platform's label: a photo the platform sent under an unlabelled attachment type is image, and a story mention that is really an MP4 is video. type: "file" means we fetched the media but could not classify it (treat it as an opaque download). type: "link" means the platform's URL did not point at media, for example a shared post that resolves to an instagram.com page; url is then the platform's own link, not re-hosted, and may require login or expire.
A note on conversation IDs
Conversation IDs are opaque strings. Some, LinkedIn especially, contain characters like : and () (for example linkedin_comment_urn:li:activity:7484628878586646528). Always URL-encode the ID when you place it in a request path. Our SDKs handle this for you.
Post context on comments
Comment and mention conversations carry the post they belong to, on the conversation and on every message in it, so a reply can be drafted with the post in view: "what size is this?" needs the caption and the picture. DMs have post: null.
Code
| Field | Description |
|---|---|
id | The platform's id of the post |
caption | The post text, when known |
thumbnail | Image URL of the post, or the video's cover |
url | Public link to the post on Instagram, Facebook, YouTube, TikTok, LinkedIn, or Threads, when the platform provides one. null otherwise |
media_type | The platform's own label for the post, for example IMAGE, VIDEO, or CAROUSEL_ALBUM on Instagram, when known. null otherwise |
Instagram stores the link and media type when the comment arrives. Instagram conversations that reached the inbox before September 6, 2026 get url and media_type filled in the first time you read them through the API.
Marking a conversation read
Marks every unread incoming message in the conversation as read.
Code
Response:
Code
Marking read also takes the conversation out of the work queue for good, so this is how you skip an item you do not want to answer.
Replying
Sends a reply in an existing conversation. Works for DMs, comments, and mentions. The reply text caps at 2,000 characters (150 on TikTok comments); longer text returns 400 validation_error.
Code
Response:
Code
On Facebook and Instagram DMs you can attach media by passing attachment_url together with attachment_type (image, video, audio, or file). text is optional when attachment_url is set: an attachment-only reply is allowed. Other platforms are text-only; attachment_url is ignored there.
Chaining to the next item
Pass include_next: true in the body and the response also carries next, the next conversation that needs an answer (same shape as GET /inbox/next, with its default order and filters), and remaining, the number of unanswered items still waiting after it. This saves the extra call when you are working through the inbox.
Code
Code
next is null and remaining is 0 when nothing is waiting. The reply itself is never affected: if the queue lookup fails, the reply still goes out and next comes back null.
Reply rules
Platform and policy limits apply. Handle these responses:
| Situation | Response |
|---|---|
| The conversation does not exist in this workspace | 404 not_found |
| The comment or message you reply to was deleted, hidden, or restricted on the platform | 404 not_found |
text is longer than the platform allows (2,000 overall, 1,000 on Instagram DMs, 150 on TikTok comments) | 400 validation_error |
| Replying to a DM more than 24 hours after the user's last message | 422 outside_messaging_window |
| The workspace has sent 1,000 replies in the last 24 hours | 429 reply_limit_reached |
| The key or role lacks inbox access | 403 insufficient_scope |
| The platform rejected the reply | 502 platform_error |
| The connection lost a needed permission (TikTok comments, LinkedIn, or the Threads reply permission) | 401 reauth_required |
| An X DM reply, but the credit balance cannot cover it | 402 insufficient_credits |
| The workspace's X inbox is paused because credits ran out | 402 x_inbox_suspended |
You can only reply to conversations that already exist in your inbox. The API cannot start a new conversation: Instagram requires the person to message you first, and cold outreach is against platform policy. Direct-message replies must be sent within the platform's 24-hour messaging window. Each workspace can send up to 1,000 replies per rolling 24 hours; contact [email protected] if you need a higher limit.
Working through the inbox
GET /inbox/next is a work queue. It returns the one item that has waited longest for an answer, together with the conversation so far and the post it belongs to, so you can draft a reply from a single call.
Code
Response:
Code
messageis the unanswered incoming message itself: the person's latest DM, or the specific comment. Itsidis what the hide and delete endpoints take; itsconversation_idis what the reply endpoint takes.messagesis the conversation so far, oldest first (the most recent 50 messages for long DM threads).remainingis how many unanswered items are still waiting after this one, capped at 500.dataisnullandremainingis0when the queue is empty.
What counts as unanswered
- A DM needs an answer when the person's latest message has no reply after it. Instagram and Facebook DMs are only included within the 24-hour messaging window, since Meta refuses replies outside it.
- A comment or mention needs an answer when nobody replied to it and it is not hidden.
- Replies sent from the native apps count as answers (see Replies sent from the native apps), so a thread a colleague answered on their phone is not served again.
- Instagram mentions are skipped, there is no reply path for them.
- The queue looks at the last 30 days of activity.
The same rules power the unanswered=true filter on GET /inbox/conversations, which lists everything waiting at once rather than serving one item at a time. The list filter ignores read state; the queue serves unread items only by default.
Skipping an item
Only unread items are served by default, so marking a conversation read (POST /inbox/conversations/{id}/read) is the durable skip: the item drops out of the queue and stays out. Pass include_read=true to also serve items that were marked read but never answered, for example for an end-of-day sweep. For a temporary skip within one session, pass exclude with a comma-separated list of conversation ids (up to 100); those are left out of that call only.
Query parameters
All optional.
| Parameter | Description |
|---|---|
platform | Only items from one platform: instagram, facebook, linkedin, tiktok, youtube, x, or threads |
type | Only items of one type: dm, comment, or mention |
order | oldest (default) serves the item that has waited longest first; newest serves the most recent first |
include_read | true also serves items that were marked read but never answered |
exclude | Comma-separated conversation ids to leave out of this call, up to 100 |
An unknown platform, type, or order returns 400 validation_error.
The loop
- Call
GET /inbox/next. - Draft a reply from
data.message,data.messages, anddata.conversation.post. - Send it with
POST /inbox/conversations/{conversation_id}/replyandinclude_next: true. The response carries the sent reply plusnextandremaining. - Repeat from step 2 with
nextuntil it isnull.
To skip an item, mark it read (for good) or add it to exclude (this session only). To deal with spam, hide or delete it; a hidden comment no longer counts as unanswered.
Hiding and deleting comments
Comments people leave on your posts can be moderated from the API, as the post owner. Both endpoints take the message id from the conversation messages list or from GET /inbox/next, not the conversation id, and need the inbox:write scope (API keys) or the access_social_inbox permission (OAuth/MCP).
| Platform | Hide | Delete |
|---|---|---|
| Yes | Yes | |
| Yes | Yes | |
| TikTok | Yes | Yes |
| YouTube | Yes, through YouTube's moderation status | No. YouTube's API does not let a channel delete other people's comments; hide instead |
| Threads | Yes, top-level replies on your posts only | No |
| No | No |
Hiding a comment
Code
The body is optional; hide defaults to true, and { "hide": false } unhides. The response returns the updated message under data with hidden flipped. The comment keeps its place in the conversation, disappears from public view on the platform until you unhide it, and no longer counts as unanswered.
On YouTube, hiding sets the comment's moderation status to rejected, which removes the comment and its replies from public view; unhiding publishes it again. On Threads, only incoming top-level replies can be hidden; Threads does not allow hiding nested replies.
| Situation | Response |
|---|---|
| The message is not an incoming comment on a platform that supports hiding | 400 unsupported_platform |
| The Threads reply is nested, or Threads refused | 400 not_hideable |
| The Threads reply permission or the TikTok comments authorization is missing or expired | 401 reauth_required |
The account was connected without the comment-moderation permission (Facebook pages_manage_engagement, Instagram instagram_business_manage_comments); reconnect it under Settings -> Organisation -> Workspaces | 403 reconnect_required |
| The message does not exist in this workspace | 404 not_found |
| No matching account connected to the workspace | 404 account_not_connected |
| YouTube's daily API quota is used up; retry after midnight Pacific | 429 quota_exceeded |
| The platform rejected the call | 502 platform_error |
Deleting a comment
Deletes the comment on the platform and from the inbox. Facebook, Instagram, and TikTok comments only. This cannot be undone.
Code
Response:
Code
Replies under the deleted comment go with it: the platforms cascade the delete and the inbox mirrors that, and their inbox ids come back in removed_reply_ids. A comment that is already gone on the platform is still removed from the inbox.
| Situation | Response |
|---|---|
| The message is not an incoming Facebook, Instagram, or TikTok comment | 400 unsupported_platform |
| The TikTok comments authorization expired | 401 reauth_required |
| The account was connected without the comment-moderation permission; reconnect it under Settings -> Organisation -> Workspaces | 403 reconnect_required |
| The message does not exist in this workspace | 404 not_found |
| No matching account connected to the workspace | 404 account_not_connected |
| The platform rejected the call | 502 platform_error |
Replies sent from the native apps
Replies you or a colleague send from the platform's own app are mirrored into the inbox as outgoing messages, so the thread in OmniSocials is complete and the person's message counts as answered. This covers Instagram DMs sent from the Instagram app, Messenger replies sent from Messenger, Meta Business Suite, or another tool, and comment replies posted natively on Instagram and Facebook. The mirrored reply marks the person's message as replied, which also takes it out of the work queue and the unanswered filter.
Facebook Pages connected before September 6, 2026 need a one-time reconnect under Settings -> Organisation -> Workspaces to start receiving Messenger replies sent from the native apps, because the Page's webhook subscription gained a new field (or contact support to have it switched on without a reconnect). Instagram works without a reconnect.
Platform notes
| Platform | What is supported |
|---|---|
| DMs, comments, mentions, and story mentions. The person must have messaged you first to open a DM thread. DM replies can include a media attachment. Comments can be hidden and deleted. Replies sent from the Instagram app are mirrored into the inbox. | |
| Page DMs (Messenger), comments, and mentions. DM replies can include a media attachment. Comments can be hidden and deleted. Replies sent from Messenger or Business Suite are mirrored into the inbox (Pages connected before September 6, 2026 need a reconnect). | |
| Company page comments and mentions only. LinkedIn has no DM API, and personal profiles are not supported. Replies post as the organization. No hide or delete. | |
| TikTok | Video comments only, after comments are enabled on the TikTok channel. Replies are text only, 150 characters max, and TikTok holds fresh comments and replies in spam review for roughly 15 to 20 minutes. Comments can be hidden and deleted. See TikTok comments. |
| YouTube | Video comments only, YouTube has no DMs. Conversations group per video, and replies are supported. Comments can be hidden through YouTube's moderation status, but not deleted. YouTube has no comment webhooks, so OmniSocials checks each channel for new comments once per day; a new comment can take up to a day to reach the inbox. The standard YouTube connection already covers comments, no extra authorization needed. |
| X (Twitter) | DMs only, after the workspace opts in. Uses credits. See X direct messages. |
| Threads | Replies people leave on your posts, and mentions. Replies publish as native Threads replies, and top-level replies on your posts can be hidden. Currently rolling out. See Threads replies and mentions. |
X direct messages
X DMs work differently from the other platforms because X charges for its DM API. They are off by default, and each workspace opts in separately.
Turning it on
In the app, open the Social Inbox and use the X DMs card. Enabling requires:
- An X account connected to the workspace, with DM permissions on the connection. Accounts connected before DM support shipped need a quick reconnect first; the card tells you when that is the case.
- The billing permission, since X DMs spend your organisation's credits.
- A credit balance of at least 25 credits.
What it costs
X bills its DM API per use. We pass those rates through as credits (purchased at €0.01 per credit), rounded up to whole credits:
| Action | Cost |
|---|---|
| DM received | 1 credit ($0.010, X's delivery rate) |
| DM reply sent | 2 credits (covers X's $0.015 send fee) |
A reply that fails to send is refunded automatically.
Running out of credits
If the balance hits zero, the X inbox pauses itself: the message that used the last credit still lands in your inbox, but new DMs stop arriving and the person who enabled X DMs gets an email. Top up and re-enable X DMs to resume. DMs sent to you while paused are not recovered.
No history, DMs only
The inbox receives DMs from the moment you enable it. Conversations from before you enabled are not imported, and neither are messages that arrived while the inbox was paused. X conversations are always type dm: comments and mentions on X are not part of the inbox, and replies are DM replies only.
TikTok comments
TikTok comments need their own authorization on top of the regular TikTok connection, so they are off until you enable them per channel.
Turning it on
In the app, go to Settings -> Organisation -> Workspaces and press Enable comments on the TikTok channel card. It runs a second TikTok authorization for the comment permissions. From then on, new comments on your videos land in the inbox in real time.
Comments only
TikTok conversations are always type comment: DMs and mentions on TikTok are not part of the inbox. Commenter names and avatars can briefly show as "TikTok User" until TikTok releases the commenter's identity. You can also like and unlike comments from the inbox in the app; liking is not available through the API yet. Comments can be hidden and deleted through the API, see Hiding and deleting comments.
Reply rules
Replies post from the connected TikTok account and are text only, capped at 150 characters. A longer reply, or one with an attachment, returns 400 validation_error. If the comments authorization expires, replies return 401 reauth_required; press Enable comments on the channel card again to fix it.
Spam review delay
TikTok holds fresh comments and replies in spam review for roughly 15 to 20 minutes before they are publicly visible on TikTok. A reply that is accepted by the API will appear under the video, just not immediately.
Threads replies and mentions
Rolling out. The Threads inbox needs Threads permissions that Meta is still reviewing for OmniSocials. Until that review passes, Threads conversations do not appear and Threads replies and hides return a clear 400 saying the feature is not available yet. Once enabled, Threads accounts connected before that date need a one-time reconnect under Settings -> Organisation -> Workspaces.
Threads brings two conversation types into the inbox:
- Replies on your posts (
type: "comment"): every reply someone leaves under one of your Threads posts. Conversations group per root post (conversation_id=threads_comment_<postId>). - Mentions (
type: "mention"): posts that mention your Threads profile (conversation_id=threads_mention_<postId>).
Threads has no DM API, so there are no Threads conversations of type dm.
Reply rules
Replying to a Threads conversation publishes a native Threads reply from your connected account: under the person's reply on your post, or under the post that mentioned you. Replies are text only. If the Threads connection is missing the reply permission (connections made before the permission was added), replies return 401 reauth_required; reconnect Threads under Settings -> Organisation -> Workspaces to fix it.
Hiding a reply
As the post owner, you can hide (or unhide) a reply someone left on one of your Threads posts with the same POST /inbox/messages/{id}/hide endpoint described under Hiding and deleting comments. Threads specifics: only incoming top-level replies can be hidden (a nested reply returns 400 not_hideable), a missing reply permission returns 401 reauth_required, and there is no delete for Threads.
Using it from an AI assistant
The inbox is available through the OmniSocials MCP server, so you can connect Claude, ChatGPT, Cursor, or any MCP client and manage your inbox in natural language. The tools are:
| Tool | What it does |
|---|---|
list_inbox_conversations | List conversations; accepts platform, type, unread, and unanswered |
get_inbox_conversation | Read a thread with the post it belongs to (caption, link, image); every message shows its #id, which the hide and delete tools take |
get_next_unanswered | The work queue: the next item that needs an answer, with the thread and the post, in one call. Accepts platform, type, order, include_read, and exclude |
mark_inbox_read | Mark a conversation read, which also skips it in the queue for good |
reply_to_inbox | Send a reply; pass include_next: true to get the next unanswered item back in the same call |
hide_inbox_comment | Hide or unhide a comment on Instagram, Facebook, TikTok, YouTube, or Threads |
delete_inbox_comment | Delete an Instagram, Facebook, or TikTok comment, with its replies. Irreversible, so the assistant confirms first |
Ask for "work through my unanswered comments" and the assistant runs the loop from Working through the inbox: get_next_unanswered, draft a reply with the post in view, reply_to_inbox with include_next: true, repeat until nothing is waiting. See MCP Server, Claude, and ChatGPT.
Every SDK also ships an inbox resource. See SDKs.