OmnioChat
← All posts

Why your Instagram DMs never reach your inbox tool

By Super Admin

If you have connected an Instagram professional account to a third-party inbox and messages are not arriving, you are probably looking at a screen that says everything is fine. The channel connected. No error appeared. The account is listed. And nothing comes through.

We spent most of a week on this. The cause turned out to be mundane, but finding it was not, because every layer that can break reports success, and Meta's documentation describes the layers separately without ever saying they all have to line up.

Here is the whole picture, in the order worth checking.

First: is Meta even trying?

Before touching configuration, find out whether Instagram is sending anything at all. Everything else is a guess until you know this.

On your server, look at the access log for requests from Meta:

grep -i facebookplatform /var/log/nginx/access.log | tail

You are looking for two different things:

  • A GET with hub.mode=subscribe answered 200. That is the verification handshake, and it only proves your callback URL and verify token are right.
  • A POST. That is an actual message.

A verified callback with no POSTs after it is the signature of this problem. It rules out, in one line, everything people usually suspect: a wrong callback URL, a bad signature, an ID mismatch, a parsing bug. All of those would still produce a POST. If there is no POST, your code has never been given the chance to be wrong.

Layer 1: the app-level subscription

In the App Dashboard, your app subscribes to webhook objects and, within each object, to fields.

These are two separate actions, and only the first one gives you feedback. Saving a callback URL triggers a verification request and shows you a green tick. Subscribing to the messages field is a different control in a different list, and skipping it produces an app that looks completely configured and receives nothing.

Confirm it from the API rather than the dashboard, which is easy to misread:

curl -s -G "https://graph.facebook.com/v26.0/{app-id}/subscriptions" \
  --data-urlencode "access_token={app-id}|{app-secret}"

You want to see:

{"object":"instagram","active":true,"fields":[{"name":"messages"}]}

If fields is empty or absent, that is your answer.

Layer 2: the Page subscription

Instagram messaging through the Messenger Platform is subscribed against the Facebook Page the Instagram account is linked to, not against the Instagram account:

POST /{page-id}/subscribed_apps?subscribed_fields=messages

Two traps here.

The field names are Page names, not Instagram names. Instagram's read-receipt field is called messaging_seen at the app level and message_reads at the Page level. Send the wrong one and Meta rejects the entire call — one bad field takes the whole subscription with it, including messages. You get a connected channel that receives nothing.

Meta's error is unusually helpful when this happens: it lists every permitted value. Read it rather than guessing.

And a success here means less than it looks. Your tool probably records that this call returned 2xx and shows the channel as healthy. That flag says the Page was subscribed. It says nothing about the app-level subscription, the setting below, or the sender.

Check what Meta actually recorded:

GET /{page-id}/subscribed_apps

Layer 3: the setting nobody mentions

In the Instagram app, on the professional account:

Settings and privacy → Messages and story replies → Connected tools → Allow access to messages

It is off by default.

With it off, Instagram delivers nothing to any third-party tool. No error, no warning, nothing in any log on either side. Your subscriptions are correct, your Page is subscribed, your endpoint is healthy, and Instagram simply does not send.

No API exposes this. Your tool cannot read it, cannot warn you about it, and cannot distinguish it from any other reason messages are not arriving. If you are building something, the best you can do is tell people it exists.

Layer 4: who is sending

This is the one that caught us, after all three above were verified correct.

Before App Review approves instagram_manage_messages, Meta only delivers messages from accounts that hold a role on your app — administrator, developer or tester. A message from anyone else is dropped before your server is contacted.

So testing with a friend's account, or your own personal Instagram, produces exactly the same silence as a broken subscription.

Two details matter:

The role goes on the sender, not the business account. Connecting the professional account is the business half. The restriction is about the member of the public on the other end — which is the entire point of it, since an unapproved app should not be able to read strangers' messages. We added the receiving account as a tester and wondered why nothing changed.

An invitation must be accepted. Instagram Tester invitations sit in Settings and privacy → Apps and websites → Tester invites on the invited account. An invitation that has been sent but not accepted behaves identically to no invitation at all, and the dashboard shows it as Pending in a column that is easy to skim past.

The order worth checking them in

  1. Access log. Any POST at all? If yes, skip everything above — your problem is parsing or routing, not delivery.
  2. GET /{app-id}/subscriptions. Is instagram there with messages in its fields?
  3. GET /{page-id}/subscribed_apps. Is your app there, with messages?
  4. The Instagram app toggle. Allow access to messages.
  5. The sender. Does that account hold an accepted role on your app?

Four of those five are invisible from your own application. That is the real lesson: when an integration like this fails, the instinct is to read your own code, and your own code is the one layer you can already see.

Why the silence

It is tempting to read this as Meta being careless. It is more that each layer is correct in isolation. The app-level subscription genuinely is app-level. The Page subscription genuinely belongs to the Page. The account toggle genuinely is the account holder's choice, and telling a third-party app that a user has declined to share messages would itself leak something. The role restriction genuinely protects people from unapproved apps.

What is missing is any single place that says all four of these are true, so messages will arrive. Until there is, the access log is the closest thing — it at least tells you which side of the wire to look at.


We hit every one of these building OmnioChat, a shared team inbox for WhatsApp, Messenger and Instagram. The badge in our channel list that says "Not receiving" exists because of layer 2 — it is the only one of the four we can detect.