Message requests
Resolve Instagram or Facebook message recipients, respect messaging windows, and verify an authorized send without duplicates.
Compact reference for agents making REST requests. All endpoint paths below are relative to https://backend.blotato.com/v2. For task instructions, read the workflow guide. For tool calls, use the MCP reference.
Manage private messages
Read the conversation and incoming message before sending. Match the account, platform, recipient, and Facebook Page to the user's request. Stop if the recipient ID is unavailable. A request to draft a reply does not authorize sending it.
Messages are private, direct messages sent through the platform inbox (DMs). Read conversations and messages, and send a direct message or a private
reply to a comment.
Supported platforms: instagram, facebook
Notes:
- Blotato does not backfill direct messages. It captures new messages received after the account connects with the required permissions.
- Refresh the Inbox after a new message arrives.
- Messages are retained for up to 45 days.
- Standard DMs reply to people who messaged you. Private replies answer a comment.
No cold outreach.
- Platforms apply strict messaging windows. Review "MESSAGING WINDOWS" before sending messages.
- Reaching a new contact counts toward the monthly active-contacts limit.
More messages to the same active contact do not add another contact that month.
Read contacts and contactsLimit from GET /users/me/usage for the current limit.
List conversations:
GET /conversations?limit=20&cursor=OPTIONAL_CURSOR&accountId=OPTIONAL_ACCOUNT_ID&platform=OPTIONAL_PLATFORM
Response: { "items": [ <Conversation>, ... ], "cursor": "..." }
Get a conversation:
GET /conversations/:conversationId
Response: a <Conversation> object
<Conversation>:
{
"id": "cnv_abc123",
"accountId": "98434",
"platform": "instagram",
"participants": [
{ "id": "1784000000", "name": "Jamie", "username": "jamie", "profileImageUrl": null }
],
"createdAt": "2026-07-01T12:00:00Z",
"updatedAt": "2026-07-02T09:30:00Z"
}
List messages:
GET /messages?limit=20&cursor=OPTIONAL_CURSOR&conversationId=OPTIONAL_CONVERSATION_ID&platform=OPTIONAL_PLATFORM
Response: { "items": [ <Message>, ... ], "cursor": "..." }
Send a message only when sending is authorized:
POST /messages
{
"accountId": "required-account-id",
"recipientId": "id-of-contact-to-send-message-to",
"text": "Thanks for reaching out!",
"target": { "targetType": "instagram" }
}
Response: a <Message> object. Save response.id and inspect response.status.
An accepted message is not proof of sending, delivery, or reading.
PLATFORM-SPECIFIC FIELDS:
instagram:
text limit: 1000 bytes
Direct message, reply to a contact within the 24-hour window:
recipientId: the incoming message's senderId
target.targetType: "instagram"
Private reply to a comment, once, within 7 days of the comment:
recipientId: the comment's authorId
target.targetType: "instagram"
target.commentId: "Blotato-comment-id-not-platformCommentId"
facebook:
text limit: 2000 characters
target.pageId (the Facebook Page to send from) is required on every Facebook message
Direct message, reply to a contact within the 24-hour window:
recipientId: the incoming message's senderId
target.targetType: "facebook"
target.pageId: "id-of-page-to-send-from"
Private reply to a comment, once, within 7 days of the comment:
recipientId: the comment's authorId
target.targetType: "facebook"
target.pageId: "id-of-page-to-send-from"
target.commentId: "Blotato-comment-id-not-platformCommentId"
Poll: GET /messages/:messageId
Status values: "queued" | "processing" | "sent" | "delivered" | "failed"
- sent: an outgoing message was sent, not proof the recipient read it
- delivered: an incoming message was received
- failed: stop and inspect errorMessage and errorCode
- queued or processing: check the saved ID within a bounded wait
When the wait budget ends, report the saved ID and pending state. Do not send again.
If no ID was received, inspect existing messages and report the uncertain outcome
before repeating a write.
<Message>:
{
"id": "msg_abc123",
"conversationId": "cnv_abc123", // null when not linked to a conversation
"platform": "instagram",
"direction": "incoming", // "incoming" received, "outgoing" you sent
"senderId": "1784000000", // null if unknown
"recipientId": "17841400000000000",
"text": "Hi! Is this available?",
"payload": null, // rich content, see BUTTONS AND QUICK REPLIES
"status": "delivered",
"errorCode": null,
"errorMessage": null,
"createdAt": "2026-07-02T09:30:00Z"
}
BUTTONS AND QUICK REPLIES (instagram, facebook):
Set on target. Mutually exclusive - send buttons OR quickReplies, never both (422).
Buttons (1-3, pinned under the message):
"target": {
"targetType": "instagram",
"attachment": {
"type": "button",
"buttons": [
{ "type": "web_url", "title": "View pricing", "url": "https://example.com" },
{ "type": "postback", "title": "Talk to sales", "payload": "SALES" }
]
}
}
- type: "web_url" (opens a link) or "postback" (reports the tap back). No phone buttons.
- title: 1-20 characters
- url: required for web_url, starts with https://
- payload: required for postback, 1-1000 characters
- text is REQUIRED and capped at 640 characters when buttons are set
- INSTAGRAM: buttons render in the Instagram mobile app only. A recipient reading
the conversation on instagram.com in a desktop browser sees the text with no
buttons. Send an attachment OR a URL inside text, never both - a message carrying
both leaves the URL in text unclickable on desktop. For a desktop audience, drop
the attachment and put the URL in text.
Quick replies (1-13 tappable chips, disappear after a tap or a typed reply):
"target": {
"targetType": "instagram",
"quickReplies": [
{ "title": "Small", "payload": "SIZE_S" },
{ "title": "Large", "payload": "SIZE_L" }
]
}
- title: 1-20 characters
- payload: 1-1000 characters
- text is REQUIRED and non-empty
Reading a tap: both chip taps and postback taps arrive as the next INCOMING
message carrying payload.selection, but they reach you differently:
- Quick reply: the tap sends a REAL TEXT MESSAGE. text holds the chip label,
and payload.selection is attached alongside it.
{ "text": "Large",
"payload": { "selection": { "type": "quick-reply", "payload": "SIZE_L" } } }
- Postback: NO text message is sent. The tap is recorded as an incoming
message with text set to the button label, plus payload.selection.
{ "text": "Talk to sales",
"payload": { "selection": { "type": "postback", "payload": "SALES" } } }
- Branch on selection.payload, NOT on text. Two chips sharing a label are
distinguishable only by payload.
- selection.payload is the string returned with the incoming tap. It is nullable
when the platform webhook supplies no payload. When creating a postback button
through POST /messages, payload is required and must contain 1-1000 characters.
- web_url buttons produce NO selection
- Blotato subscribes your account to the postback webhook when you connect it.
If you connected your account before buttons were launched, reconnect in
Accounts to start receiving postback events. Quick replies
need no reconnect.
Outgoing messages you sent echo their own payload.attachment / payload.quickReplies.
MESSAGING WINDOWS:
- Standard DM: reply within 24 hours of the person's last message.
- Private reply to a comment: set target.commentId. Allowed once, within 7 days of the
comment. After it, message the person again only after they open a new 24-hour window.Return to the agent reference index.
Last updated