Direct messages: chats, private replies, and ig.me referrals via API
Postproxy now reads and sends 1:1 direct messages on Facebook Messenger, Instagram, Telegram, and Bluesky — including private replies that turn a comment into a DM and ig.me / m.me link referrals with campaign attribution.
Postproxy now supports direct messages on Facebook Messenger, Instagram, Telegram, and Bluesky. The Direct Messages API reads and sends 1:1 messages, turns comments into DMs via Meta's Private Replies, and attributes conversations started from ig.me / m.me links through a new referral.received webhook.
What changed
The Direct Messages API went live in early June and has been filling out since:
- Chats and messages. A conversation between your profile and a participant is a Chat; each chat holds inbound and outbound Messages. List chats, list messages, send text or media, react (Facebook and Instagram), edit (Telegram), archive (Bluesky).
- Private replies to comments.
POST /api/posts/:post_id/comments/:id/private_replyDMs the author of a specific comment — the mechanism behind every “comment GUIDE and I’ll send you the checklist” funnel. - ig.me / m.me link referrals. When someone enters your DM thread via
https://ig.me/m/<username>?ref=summer_promo, therefis stored on the chat and areferral.receivedwebhook fires — campaign attribution for DM conversations. - Ice breakers. Manage the up to four suggested questions Instagram shows when a user opens a new thread with your business.
- Inbound delivery via webhooks. New messages, edits, reactions, delivery and read receipts, story mentions, and referrals all arrive as webhook events — no polling loop on Meta networks.
A redesigned Chats UI in the app ties it together, but everything below is about the API.
Which platforms support DMs?
Four networks, with capabilities varying by platform:
| Capability | Telegram | Bluesky | ||
|---|---|---|---|---|
| Send / receive text | Yes | Yes | Yes | Yes |
| Attachments | Yes | Yes | Yes | No |
| Reactions | Yes | Yes | No | No |
| Edit outbound message | No | No | Yes | No |
| Delivery / read receipts | Yes | Yes | No | No |
| Private reply to comment | Yes | Yes | No | No |
| ig.me / m.me link referrals | Yes | Yes | No | No |
| Ice breakers | No | Yes | No | No |
| Inbound delivery | Webhook | Webhook | Webhook | Poller (~5 min) |
Telegram DMs run over the Bot API and add reply threading and inline/reply keyboards — button taps arrive as inbound messages. Bluesky DMs go through the AT Protocol chat service, with inbound messages polled roughly every five minutes. Platform-specific behavior is documented in the Telegram notes and Bluesky notes.
How do you list conversations?
GET /api/profiles/:profile_id/chats returns a profile’s conversations, ordered by last_message_at descending — an inbox, most recent first:
curl "https://api.postproxy.dev/api/profiles/PROFILE_ID/chats?per_page=20" \ -H "Authorization: Bearer YOUR_API_KEY"{ "total": 12, "page": 0, "per_page": 20, "data": [ { "id": "chat_xyz789", "profile_id": "prof_abc123", "platform": "instagram", "participant_external_id": "igsid_8675309", "participant_username": "jane_doe", "participant_name": "Jane Doe", "participant_avatar_url": "https://storage.postproxy.dev/.../chat_avatar_42.jpg", "last_inbound_at": "2026-06-11T14:02:00.000Z", "last_outbound_at": "2026-06-11T15:10:00.000Z", "last_message_at": "2026-06-11T15:10:00.000Z", "metadata": { "is_verified_user": false, "is_user_follow_business": true, "is_business_follow_user": false, "follower_count": 482, "participant_fetched_at": "2026-06-11T15:10:05Z" }, "created_at": "2026-04-12T08:00:00.000Z" } ]}Two things in that payload do real work:
last_inbound_attells you whether the 24-hour messaging window is open — compare it against the current time before attempting a free-form send on Meta networks.metadatacarries participant context fetched from the platform — whether the user follows your business, their follower count, verified status. Enough to route VIPs to a human and let automation handle the rest, without an extra lookup.
before / after filters take ISO 8601 timestamps against last_message_at, and GET /api/chats/:chat_id/messages pages through a single conversation’s history, filterable by direction (inbound / outbound) and status.
How do you send a message?
POST /api/chats/:chat_id/messages with a body:
curl -X POST "https://api.postproxy.dev/api/chats/CHAT_ID/messages" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "body": "Yes, we ship worldwide!" }'The response is 202 Accepted — sends are asynchronous. The message comes back immediately with status: "pending", flips to "published" once the platform confirms (populating external_id), and on failure goes to "failed_waiting_for_retry" (retried with backoff) or "failed":
{ "id": "msg_222", "chat_id": "chat_xyz789", "external_id": null, "direction": "outbound", "body": "Yes, we ship worldwide!", "status": "pending", "external_posted_at": null, "attachments": [], "created_at": "2026-06-12T15:30:05.000Z"}Subscribe to message.sent / message.failed webhooks instead of polling for the status flip.
To send media, pass media instead of body — same shapes as the Posts API (multipart upload, public URL, base64):
curl -X POST "https://api.postproxy.dev/api/chats/CHAT_ID/messages" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "media": ["https://cdn.example.com/photo.png"] }'One attachment per send (a Meta Send API limit), and each send is either text or media, not both. Attachments are copied to durable storage before dispatch, so the url on the message never expires.
For a participant the profile hasn’t messaged yet, POST /api/profiles/:profile_id/chats with their platform ID (Instagram-scoped user ID, Facebook PSID, or Bluesky DID) finds or creates the chat idempotently — then send into it as above.
How do private replies to comments work?
A private reply sends a DM to the author of a comment, in response to that specific comment, using Meta’s Private Replies mechanism. Three properties make it different from a regular send:
- It bypasses the 24-hour messaging window — you can private-reply to comments up to 7 days old (Meta-enforced).
- It needs no existing chat — Postproxy creates or reuses one, keyed by the comment’s author.
- Meta allows one private reply per comment, ever.
It works on Instagram and Facebook Page comments:
curl -X POST "https://api.postproxy.dev/api/posts/POST_ID/comments/COMMENT_ID/private_reply?profile_id=PROFILE_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Here is the checklist: https://yoursite.com/checklist" }'The response is 202 Accepted with a Message object whose chat_id points at the chat with the commenter. If the user replies, that opens a normal 24-hour messaging window and the conversation continues free-form over the regular send endpoint.
Pair it with the comment.created webhook and you have the full comment-to-DM funnel — keyword match in, DM out — without renting it from ManyChat. The step-by-step build is in How to turn Instagram comments into DMs.
What do ig.me / m.me referrals give you?
ig.me links (https://ig.me/m/<username>) and Messenger’s m.me equivalent deep-link a user straight into a DM thread with your business. Append a ref parameter to attribute the conversation:
https://ig.me/m/your_username?ref=summer_promoWhen the user enters the thread via such a link, Postproxy:
- finds or creates the chat for the participant,
- stores the referral on the chat —
chat.metadata.referralholds theref, source, and type, - opens the 24-hour messaging window (per Meta policy, a referral allows a free-form reply even before the user sends a message),
- dispatches a
referral.receivedwebhook to your subscribers.
Put a different ref in each ad, bio link, or QR code, and your webhook handler knows which campaign every DM conversation came from.
One Meta quirk worth knowing: Instagram only delivers ig.me referrals for profiles with at least one ice breaker set. Setting ice breakers is a single API call (POST /api/profiles/:id/ice_breakers), and the questions double as a structured entry point — when a user taps one, it arrives as an inbound message carrying your payload.
What about the 24-hour messaging window?
Meta only permits free-form messages within 24 hours of the participant’s last inbound message. Three things open the window:
- The user sends you a message (including an ice-breaker tap).
- The user enters the thread via an ig.me / m.me link referral.
- You send a private reply to their comment (its own 7-day window, one reply per comment).
Sending outside the window is not currently supported through the API. Telegram has no window — once a user has DM’d the bot, it can reply at any time. Bluesky has no window either; the recipient just needs DMs enabled.
How do inbound messages arrive?
Subscribe with the Webhooks API. The relevant events: message.received, message.sent, message.delivered, message.read, message.edited, message.deleted, message.failed, reaction.received, and referral.received. Webhook payloads mirror the full REST message shape, so a handler can act on an inbound DM without a follow-up fetch.
Getting started
If you already have a Postproxy account with a Facebook, Instagram, Telegram, or Bluesky profile connected:
- Call
GET /api/profiles/:profile_id/chatsto see existing conversations. - Call
POST /api/chats/:chat_id/messagesto reply. - Subscribe to
message.receivedvia the Webhooks API to react to inbound DMs.
No new scopes for existing Meta connections, no separate billing. The full endpoint reference — including media attachments, reactions, Telegram keyboards, and participant metadata — is in the Direct Messages docs.
Postproxy
One API for every social platform
Publish to Instagram, X, LinkedIn, TikTok, YouTube and more with a single request. Free plan, no credit card required.