Skip to main content
SocialAPI’s direct message (DM) API lists the conversations of your connected accounts, reads their messages, and sends replies. A conversation is one person talking to one connected account. The endpoints are the same on every platform that supports DMs: Instagram, Facebook, X, WhatsApp, Telegram, Zalo, and Bluesky. A send on any other platform returns 501 resource.not_supported. A DM request costs 1 interaction credit, however many messages it sends. Reads and reactions cost nothing. Each platform decides when you can message a person: see The messaging window.

Endpoints

{id} is the conversation id that GET /v1/inbox/conversations returns. It is a UUID, not a sapi_dm_ ID. {messageId} is a message’s platform_id. See IDs.

Your first message

List the conversations of one connected account:
Response (HTTP 200):
Send a reply into that conversation:
Response (HTTP 200):
message_ids lists the SocialAPI ID (sapi_dm_) of every message the request sent, in send order. message_id is the first one and is deprecated. On Zalo, message_id is empty and each entry of message_ids is an empty string. To get the platform ID of a sent message, read it from the message list (platform_id). A conversation that belongs to another connected account than account_id returns 404 conversation.not_found.

Conversations

GET /v1/inbox/conversations lists conversations, most recent activity first. PATCH /v1/inbox/conversations/{id} with { "status": "archived" } or { "status": "active" } changes the status and returns { "success": true }. Any other value returns 400 validation.field_invalid. POST /v1/inbox/conversations/{id}/read sets unread_count to 0 and returns { "success": true }. It does not send a read receipt to the person.

Read messages

GET /v1/inbox/conversations/{id}/messages lists the messages of a conversation, newest first.
Response (HTTP 200):
limit is 25 by default. A value above 200 is treated as 200, and a value of 0 or less, or one that is not an integer, is treated as 25. See Pagination.

Send a message

POST /v1/inbox/conversations/{id}/messages sends a message into a conversation. A request must hold text, an attachment, or interactive content, and can hold all three. A request with none returns 400 validation.field_required. SocialAPI sends text first, then each attachment in order, as separate messages. Text plus 2 images arrives as 3 messages, and the request still costs 1 credit.

Attachments

Each item of attachments names a media source:
The platform fetches a url attachment from your URL, so it must be reachable without a login. Facebook accepts files up to 25 MB. Instagram accepts images up to 8 MB and video, audio, and PDF files up to 25 MB. SocialAPI resolves every media_id before it sends anything, so an unknown or unfinished media_id returns 400 validation.media_unresolved and sends no message. If a send fails part way, the earlier messages stay delivered. The error carries error.meta.sent_count (messages sent) and error.meta.failed_index (the item that failed, counting text as item 0 when present), so you can retry from there. Some platforms accept less. X sends text only and rejects an attachment with 400 validation.attachment_unsupported. Bluesky sends only text. Zalo sends a url or media_id attachment as an image unless its type is gif or sticker. Telegram accepts attachments with captions. See X, Bluesky, Zalo, and Telegram.

Interactive messages

Instagram sends structured, tappable content. A Facebook DM send delivers only text and ignores quick_replies, buttons, and cards, so the rules in this section apply to Instagram. Buttons do work on a Facebook private reply. Set at most 1 of quick_replies, buttons, or cards per message. More than 1 returns 400 validation.interactive_conflict. When the person taps a quick reply or a postback button, SocialAPI sends a dm.postback event with the payload you set. To answer taps without your own handler, use an Automation. Interactive content follows the messaging window like any message.

Quick replies

Quick replies are up to 13 tappable chips above the keyboard. They need text. More than 13 returns 400 validation.too_many_quick_replies. A title over 20 characters returns 400 validation.quick_reply_title_too_long.

Buttons

Buttons are a text bubble with 1 to 3 buttons. text is the body and holds 640 characters at most (400 validation.text_too_long). More than 3 buttons returns 400 validation.invalid_button_count.

Cards

Cards are a carousel of up to 10 cards, each with an image, a title, a subtitle, and up to 3 buttons. More than 10 cards returns 400 validation.too_many_cards. More than 3 buttons on a card returns 400 validation.too_many_buttons. A title or subtitle over 80 characters returns 400 validation.card_text_too_long.
WhatsApp has buttons with its own rules and list menus, and Zalo takes buttons and 1 card. Their pages describe both: WhatsApp, Zalo. Telegram, X, and Bluesky do not support interactive content.

React to a message

POST /v1/inbox/conversations/{id}/messages/{messageId}/reaction adds your emoji reaction to a message, and replaces your earlier one. It works on Instagram and Facebook. Other platforms return 501 resource.not_supported.
Response (HTTP 200):
reaction is the emoji character itself, a single emoji. Anything else returns 400 validation.invalid_reaction. DELETE on the same path, with account_id as a query parameter, removes the reaction. A reaction costs no credit.

The messaging window

Most platforms let you message a person only for a limited time after their last message. A send outside the window fails, and SocialAPI sends nothing.
  • Instagram and Facebook: 24 hours from the person’s last message. Outside it, the send returns 400 platform.instagram.window_closed or 400 platform.facebook.window_closed. On Instagram Login, message_tag: "HUMAN_AGENT" extends the window to 7 days. Instagram Login accepts no other tag: any other value returns 400 validation.invalid_message_tag. A Facebook Page, and an Instagram account connected through Facebook Login, send no message_tag: the DM goes out untagged.
  • WhatsApp: a free-form message needs the 24-hour service window, and outside it the send returns 400 whatsapp.window_closed. An approved template is the way to reach the person: see WhatsApp.
  • Zalo: the person must have interacted with the Official Account in the last 7 days. Outside it, the send returns 422 platform.zalo.not_deliverable: see Zalo.
  • Telegram: the person must have messaged the bot first. Before that, the send returns 403 telegram.cannot_message: see Telegram.
After Instagram or Facebook refuses a conversation for a closed window, SocialAPI refuses further sends on it with the same code until the person messages you again or 24 hours pass. These refusals cost no credit. An Instagram Login send with message_tag is not refused this way. A send on Instagram connected through Facebook Login goes out untagged, so it is refused. A private reply does not clear the refusal. The person’s next message does.

Limits

Each DM request costs 1 interaction credit. When the allowance is used up, the call returns 429 validation.interaction_limit_exhausted. Plans and usage lists what costs a credit.

Platform support

Which platform supports DMs, attachments, interactive content, and reactions is in the capability matrix. Each platform page lists its own rules: Instagram, Facebook, X, WhatsApp, Telegram, Zalo, and Bluesky. Threads, YouTube, Pinterest, and Google Business Profile have no DMs.

Errors and debugging

Check these in order when a send fails:
  1. 401, 403 auth.insufficient_scope. The key is invalid, or a scoped key lacks dms:send.
  2. 404 conversation.not_found, 404 account.not_found, 403 account.reconnection_required. The conversation is not yours or belongs to another connected account than account_id, or the account needs reconnecting.
  3. 403 account.permission_missing. The account lacks the permission the call needs: messages.write to send a message or a reaction, and messages.read to start or read an inbox sync. error.meta.permission names it. The conversation and message lists return what SocialAPI stored and never return this error: an account without messages.read stores no new messages.
  4. 400 validation.*. The body is wrong. error.meta.field names the field. The codes are in Send a message and Interactive messages.
  5. 400 platform.instagram.window_closed, 400 platform.facebook.window_closed, 400 whatsapp.window_closed, 422 platform.zalo.not_deliverable, 403 telegram.cannot_message. The messaging window is closed: see The messaging window.
  6. 422 platform.instagram.thread_unavailable, 422 platform.facebook.thread_unavailable. The platform refused to deliver to this conversation, which can be restricted or unavailable.
  7. 403 platform.instagram.permission_denied, 403 platform.facebook.permission_denied. The platform scopes granted at connect do not cover the action. Reconnect the account and approve every scope.
  8. 429 validation.interaction_limit_exhausted, 429 platform.{platform}.rate_limit. Your allowance, or the platform’s own limit. Read Retry-After: see Rate limits.
  9. 501 resource.not_supported. The platform has no DMs, or no reactions.
When a conversation or a message is missing from a list, force a refresh with POST /v1/inbox/sync. The full catalog is in Errors.

Scopes

Events

SocialAPI sends DM events to your webhook endpoint: dm.received, dm.sent, dm.postback, dm.referral, dm.deleted, and dm.status.*. Each is described, with the platforms that emit it, on Webhook events. An account without the messages.read permission receives none of the dm.* events, except that, with messages.write alone, the account still receives dm.status.read on Instagram, dm.status.delivered and dm.status.read on Facebook, and every dm.status.* event on WhatsApp. It receives no dm.sent and no dm.status.sent on Instagram or Facebook.

Use from an AI agent

There is no tool for reactions or for inbox sync. See MCP tools.
Last modified on October 19, 2026