How to Build a Unified Social Media Inbox via API

One chat and message schema across Facebook Messenger, Instagram DMs, Telegram, and Bluesky — list conversations, receive webhooks, and reply from a single inbox API.

One schema, four platforms

The Direct Messages API normalizes Facebook Messenger, Instagram, Telegram, and Bluesky into two resources:

  • Chat — a conversation with one participant: participant_name, participant_avatar_url, last_message_at, platform-specific metadata.
  • Messagedirection (inbound/outbound), body, attachments, status, timestamps.

Your inbox renders these two objects. The platform differences — Meta webhooks vs Telegram Bot API vs AT Protocol polling — stay on Postproxy’s side of the line.

The conversation list

Pull chats per connected profile, ordered by latest activity:

Terminal window
curl "https://api.postproxy.dev/api/profiles/PROFILE_ID/chats?per_page=20" \
-H "Authorization: Bearer YOUR_API_KEY"

Merge the lists from all profiles, sort by last_message_at, and that’s the left pane. Each chat already has the participant’s display name and a stable avatar URL — no platform lookups to render a row. before / after timestamp filters handle incremental refresh.

The thread view

Terminal window
curl "https://api.postproxy.dev/api/chats/CHAT_ID/messages?per_page=50" \
-H "Authorization: Bearer YOUR_API_KEY"

Messages come newest-first with everything a thread UI needs:

  • direction decides bubble alignment.
  • attachments carry stable storage URLs — inbound media is mirrored off platform CDNs, so images don’t break when Meta’s tokens expire.
  • external_read_at / external_delivered_at give read receipts (Facebook and Instagram).
  • reactions is the live reaction state per message.
  • external_edited_at and external_deleted_at let you render “edited” and tombstones.

Real-time updates

Subscribe one webhook endpoint to the message events and fan out to your UI over your own websocket/SSE:

  • message.received — new inbound message (the event that bumps a chat to the top)
  • message.sent, message.delivered, message.read — outbound lifecycle
  • message.edited, message.deleted — thread mutations
  • reaction.received — reactions, including removals

One caveat: Bluesky has no webhooks upstream, so inbound Bluesky messages arrive via a ~5-minute poller. The message.received event still fires — just with up to 5 minutes of latency.

Replying

Same send call regardless of platform:

Terminal window
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": "On it — refund issued, you should see it in 3-5 days." }'

Track the returned message’s status (pendingpublished, or failed with error_details) to show send state in the composer, or let message.sent / message.failed webhooks drive it.

Capability differences to surface in the UI

The schema is uniform; platform abilities aren’t. Disable composer features per chat’s platform:

CapabilityFacebookInstagramTelegramBluesky
AttachmentsYes (1/send)Yes (1/send)YesNo
ReactionsYesYesNoNo
Edit sent messageNoNoYesNo
Read receiptsYesYesNoNo
Free-form send window24h after last inbound24h after last inboundAny time after first user DMAny time

The 24-hour rule is the one to design around: when a Meta chat’s last_inbound_at is older than 24 hours, grey out the composer — the send would fail anyway. (Private replies are the comment-triggered exception.)

Beyond DMs

The same inbox pattern extends to public interactions: comments on Instagram, Facebook, and Threads, and Google Business reviews — all webhook-driven, all replied to with one POST. Per-platform send guides: Instagram DMs, Facebook Messenger.

Ready to get started?

Start with our free plan and scale as your needs grow. No credit card required.