Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meta Cloud API is triggering every webhook for multiple Apps

I am currently facing an issue with the Meta Cloud API and WhatsApp Business API and I'm seeking some guidance.

Issue Description:

I have a WhatsApp chatbot for customers, and to facilitate testing and development, I've set up three different environments (stages). Each stage has its own test number and is associated with a separate app using the Meta Cloud API and WhatsApp Business API. Every app is connected to its unique webhook and is subscribed to the 'messages' field.

However, I am encountering an unexpected behavior where every webhook is receiving events from all other numbers, not just its corresponding number.

Setup Details:

For each app, I have the following configuration in the meta developer web app:

Quickstart Configuration: To get alerted when you receive a message or when a message's status has changed, a Webhooks endpoint is set up for each app.

Callback URL: Each app has a different callback URL, something like https://.....com/webhook.

Verify token: Different for each app

What actually happens:

Despite having distinct endpoints and configurations for each app, messages intended for one stage are being received by the webhooks of the other stages as well.

Object received from Meta Cloud API when I send "hi" to the test number – gets delivered to all three webhooks:

{
    "object": "whatsapp_business_account",
    "entry": [
        {
            "id": "id_1",
            "changes": [
                {
                    "value": {
                        "messaging_product": "whatsapp",
                        "metadata": {
                            "display_phone_number": "test_stage_phone_no",
                            "phone_number_id": "test_stage_phone_no_id"
                        },
                        "contacts": [
                            {
                                "profile": {
                                    "name": "Leon"
                                },
                                "wa_id": "my_phone_number"
                            }
                        ],
                        "messages": [
                            {
                                "from": "my_phone_number",
                                "id": "wamid.HBgNNDk...",
                                "timestamp": "1704481181",
                                "text": {
                                    "body": "hi"
                                },
                                "type": "text"
                            }
                        ]
                    },
                    "field": "messages"
                }
            ]
        }
    ]
}

Object received from Meta Cloud API when I send "hi" to the production number – gets delivered to all three webhooks:

{
    "object": "whatsapp_business_account",
    "entry": [
        {
            "id": "id_2",
            "changes": [
                {
                    "value": {
                        "messaging_product": "whatsapp",
                        "metadata": {
                            "display_phone_number": "prod_stage_phone_no",
                            "phone_number_id": "prod_stage_phone_no_id"
                        },
                        "contacts": [
                            {
                                "profile": {
                                    "name": "Leon"
                                },
                                "wa_id": "my_phone_number"
                            }
                        ],
                        "messages": [
                            {
                                "from": "my_phone_number",
                                "id": "wamid.HBgNN...",
                                "timestamp": "1704482444",
                                "text": {
                                    "body": "hi"
                                },
                                "type": "text"
                            }
                        ]
                    },
                    "field": "messages"
                }
            ]
        }
    ]
}

As a temporary workaround, in my webhook, I am checking the WhatsApp Business ID from which the message originates and filtering out messages that don't match the Business ID associated with that stage. However, I believe there should be a way to ensure each webhook only receives messages for its specific app.

Questions:

Has anyone else experienced this issue with the Meta Cloud API and WhatsApp Business API?

Is there a configuration step I might be missing that would ensure each webhook only receives messages intended for its associated app? Are there any best practices or additional settings I should consider to isolate the webhook events to their respective apps?

like image 632
leonbeckert Avatar asked May 21 '26 00:05

leonbeckert


2 Answers

I followed a Facebook article and it worked perfectly! Mentioned article: https://developers.facebook.com/docs/whatsapp/embedded-signup/webhooks

If you want to receive an individual Webhook response for each APP without needing to create a new business, follow the article above.

In my case, I needed an individual Webhook for 3 numbers.

In summary, I did the following:

I checked which Webhook subscriptions existed for each of my WhatsApp Business numbers.

  • For this, I made this request 3 times, changing the ID_WHATSAPP_BUSINESS:
Method: GET
URL: https://graph.facebook.com/{{VERSION}}/{{ID_WHATSAPP_BUSINESS}}/subscribed_apps
Headers: Bearer {{TOKEN_SUPREME or TOKEN_TEMPORARY_ANY_APP}}

After checking which Webhook subscriptions existed for each WhatsApp Business number, I cleared all Webhook subscriptions for all my WhatsApp Business numbers.

  • For this, I made this request 3 times, changing the ID_WHATSAPP_BUSINESS:
Method: DELETE
URL: https://graph.facebook.com/{{VERSION}}/{{ID_WHATSAPP_BUSINESS}}/subscribed_apps
Headers: Bearer {{TOKEN_SUPREME}}

The TOKEN_SUPREME, which is a token with higher level access, was able to delete all Webhook subscriptions at once, but it's also possible to repeat the request several times by changing the token to a temporary token for each APP. This way, it's possible to delete all subscriptions for the WhatsApp Business number.

After deleting all Webhook subscriptions for all WhatsApp Business numbers, I individually added the Webhook subscription for an APP to the WhatsApp Business numbers I desired.

  • For this, I made this request 3 times, changing the ID_WHATSAPP_BUSINESS_DESIRED:
Method: POST
URL: https://graph.facebook.com/{{VERSION}}/{{ID_WHATSAPP_BUSINESS_DESIRED}}/subscribed_apps
Headers: Bearer {{TEMPORARY_TOKEN_DESIRED_APP}}

To confirm if each WhatsApp Business number had the correct APP Webhook subscription, I checked the subscriptions for each number, as before.

  • For this, I made this request 3 times, changing the ID_WHATSAPP_BUSINESS:
Method: GET
URL: https://graph.facebook.com/{{VERSION}}/{{ID_WHATSAPP_BUSINESS}}/subscribed_apps
Headers: Bearer {{TOKEN_SUPREME or TOKEN_TEMPORARY_ANY_APP}}

Expected result for each WhatsApp Business number:

{
    "data": [
        {
            "whatsapp_business_api_data": {
                "link": "https://www.facebook.com/games/?app_id=ID_APP",
                "name": "NAME_APP",
                "id": "ID_APP"
            }
        }
    ]
}

I followed these steps to add different Webhook subscriptions for each WhatsApp Business number.

* Edit:

Follow the steps below only if your Meta apps are automatically re-subscribing to Webhooks

⚠️ Important: After a few hours or days, Meta may automatically re-subscribe a WhatsApp Business to multiple apps, even after webhook subscriptions are manually removed.

Someone commented that this solution worked for a few hours, but later the WhatsApp number became subscribed again to both apps, causing webhook events to be sent to two different webhook URLs.
I observed the same behavior in earlier tests.

In my case, what seems to have definitively fixed the issue (so far) was isolating each Meta app as much as possible with its corresponding WhatsApp Business. These are the steps I followed:

  • Delete or remove all Meta apps with WhatsApp enabled that were not needed
    (It may be enough to just remove the webhook subscription, but I chose to delete the apps to avoid any hidden linkage)

  • Have exactly one Meta app per WhatsApp Business
    There doesn’t seem to be a way to explicitly tell Meta that App X is the owner of WhatsApp Business Y, but this separation is especially important when generating access tokens

  • Generate one access token per WhatsApp Business
    And when generating the token, select only the Meta app that belongs to that WhatsApp Business

  • Do not use or generate access tokens with permissions for multiple Meta apps
    I revoked all existing multi-app tokens and kept only tokens associated with a single Meta app

I currently have 3 WhatsApp numbers, each with its own webhook endpoint.
Before following these steps, they were automatically re-subscribing to other apps’ webhooks.
After applying this isolation strategy, more than 2 days have passed and no automatic webhook re-subscription has occurred.

like image 152
Rafael Gonçalves Avatar answered May 25 '26 06:05

Rafael Gonçalves


I'm not sure yet if that is a bug or a feature, but there is a workaround to fix this.

Approach we used
You want to take the message object from the webhook, and there are 2 fields that will allow you distinguish the source app from the additional triggers.

{
  "object": "whatsapp_business_account",
  "entry": [{
      "id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
      "changes": [{
          "value": {
              "messaging_product": "whatsapp",
              "metadata": {
                  "display_phone_number": "PHONE_NUMBER",
                  "phone_number_id": "PHONE_NUMBER_ID"
              },
              # specific Webhooks payload            
          },
          "field": "messages"
        }]
    }]
}

You can use WHATSAPP_BUSINESS_ACCOUNT_ID or PHONE_NUMBER_ID(if your apps have different numbers as in our case) These 2 elements will be constant for the app you send message from. Just save one of them on your side, and check if message comes from the desired app to handle it later.

Another approach
This wasn't a good workaround for us, but it is possible to have an app per business page. With this setup you will ensure 1 endpoint per whole business account and you won't have issues with other webhooks being triggered.

P.S.
The other issue you may encounter, and I didn't like it at all, is bombardment with old statuses updates. This happens, if you don't answer with 200 statuses explicitly for every webhook trigger.

From their docs: If we send a webhook request to your endpoint and your server responds with an HTTP status code other than 200, or if we are unable to deliver the webhook for another reason, we will keep trying with decreasing frequency until the request succeeds, for up to 7 days.

The workaround was to catch all the possible handling errors on our side and log them to our console, but send 200 for every request as this: response.status(200).json({ message: 'Handling finished' })

like image 27
Animus Avatar answered May 25 '26 06:05

Animus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!