Using Webhooks API to Integrate Lead Ads with CRMs

This guide goes through every step to a successful CRM integration using Webhooks. If you are a CRM partner or an independent developer looking to use the Webhooks feature to receieve updates about leads being created for your ads, read on.

Terms and Concepts

We reference the following terms multiple times. So here is a brief description of them:

  • App – The facebook app which will be configured with the webhook API
  • RTU- Stands for Real time updates. They contain the information about the lead.
  • Payload – Content that comes in the RTU. Synonymous to RTU
  • Endpoint – The end point that you (client or crm partner) configures to receive RTU
  • Pings – These are the real time updates which will come through on your endpoint
  • CRM – Customer Relationship Management


Step 1: Set up your App

  • From the facebook developers apps portal, create an app. Go to the settings tab from the navigation pane and fill out the basic information such as name, email etc.
  • Under App Domains, include your webserver’s URL. You can also add local host for testing purposes.
  • Go to the reviews tab and make your app public. Then go ahead and ask for the pages_read_engagement, pages_manage_metadata, and pages_show_list permissions and ads_management permission. There is a review process before granting these permissions. You will be notified by Facebook once it is complete or if there s more inforamtion required from you during the review.
  • Keep a note of the app_id, since you will need this in later steps.

Step 2: Subscribe the app with Facebook

This step will let Facebook know that my app is interested in leadgen event

2.1 Create a webhook.php file

On your webserver, create a file called as webhooks.php. Note that, we were referencing this file in the call back url under the app domain. The file will have the following content:

$challenge = $_REQUEST['hub_challenge'];
$verify_token = $_REQUEST['hub_verify_token'];

if ($verify_token === 'abc123') {
  echo $challenge;

2.2 Make the api call to create subscription

The following API call is made with an app access token. The callback URL should be the URL to your app’s endpoint and the verify token should be what was added in webhook.php.

curl \
  -F "object=page" \ 
  -F "callback_url=" \ 
  -F "fields=leadgen" \ 
  -F "verify_token=abc123" \ 
  -F "access_token=<APP_ACCESS_TOKEN>" \ 

If the API call is successful, it means that the app was successfully subscribed to receive leadgen RTU from Facebook. After this step you should be able to see this subscription when you navigate to the webhooks section.

Step 3: Subscribe the Page to Your App

We don’t send payloads to your apps every time someone fills a form and submits a lead on Facebook. Since the lead ads forms are associated with an advertiser’s Facebook page, we filter and send payloads only for leads that belong to those pages which authorized your app to receive the RTU.

Note: The client or advertiser who authorizes your app to listen to the RTU pings need to have ADMIN access on the page. Without this the subscription will not work.

From an advertiser’s perspective, this is how the flow will look like:

  • Client with page admin access logs into your platform
  • They connect with FB login
  • Your facebook app asks for pages_read_engagement permission from the advertiser.
  • Client with page admin access approves.

Once the above is complete then your app can receive rtu for the subscribed page.

Follow the steps below to achieve this flow:

3.1 Create platform.php file on your webserver

Once you have the file and the FB Login ready in the page, add the following code:

<h2>My Platform</h2>

  window.fbAsyncInit = function() {
      appId      : '<YOUR_APP_ID>',
      xfbml      : true,
      version    : '<API_VERSION>'

  (function(d, s, id){
     var js, fjs = d.getElementsByTagName(s)[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement(s); = id;
     js.src = "";
     fjs.parentNode.insertBefore(js, fjs);
   }(document, 'script', 'facebook-jssdk'));

  function subscribeApp(page_id, page_access_token) {
    console.log('Subscribing page to app! ' + page_id);
      '/' + page_id + '/subscribed_apps',
      {access_token: page_access_token, subscribed_fields: ['leadgen']},
      function(response) {
        console.log('Successfully subscribed page', response);
  // Only works after `FB.init` is called
  function myFacebookLogin() {
      console.log('Successfully logged in', response);
      FB.api('/me/accounts', function(response) {
        console.log('Successfully retrieved pages', response);
        var pages =;
        var ul = document.getElementById('list');
        for (var i = 0, len = pages.length; i < len; i++) {
          var page = pages[i];
          var li = document.createElement('li');
          var a = document.createElement('a');
          a.href = "#";
          a.onclick = subscribeApp.bind(this,, page.access_token);
          a.innerHTML =;
    }, {scope: 'pages_show_list'});
<button onclick="myFacebookLogin()">Login with Facebook</button>
<ul id="list"></ul>

The above code does the following:

  • When the user (advertiser/client) authenticates with Facebook they will be shown a pop up asking App XYZ will like to manage your pages.
  • Once the user gives your app the permission to manage pages, a call to the me/accounts edge to get all pages the user has access to.
  • The response to the above call contains a Data Object with series of pages and the page access token for each one.

You can also use graph API to make this call:

curl -G \
  -d "access_token=<ACCESS_TOKEN>" \

You use the Page access token later to make the API call which will subscribe the advertisers page to your app. Please note that when making the call to retrieve all pages for the user, if a long lived access token is used, then page access token returned will never expire.

  • The app then display a list of all pages such that users can click onto these to be able to proceed with requesting RTU for those pages.
  • When the advertiser clicks on the page from the list, we make a POST api call to the {PAGE_ID}/subscribed_apps edge. This is taking place in the subscribeApp function. The equivalent POST request is:
curl \
  -F "access_token=<PAGE_ACCESS_TOKEN>" \ 

3.2 Verify the Subscription

To verify that your app was successfully subscribed to receive RTU for the selected page, make the following GET API call

curl  -G \
 -d "access_token=<PAGE_ACCESS_TOKEN>" \ 

The response of this api call should contain an entry for your app. Here is a sample API call along with the response as shown in Grah Explorer.

Step 4: Modify webhooks.php for Incoming RTUs

To test if the RTU integration is working and if you are receiving pings from facebook, modify the webhooks.php file to handle the incoming pings, since this is the endpoint which wil:l receive the pings. For now we will error log it by adding the following code

$input = json_decode(file_get_contents('php://input'), true);
error_log(print_r($input, true));

After the above steps, the webhook.php file should look like this:


$challenge = $_REQUEST['hub_challenge'];
$verify_token = $_REQUEST['hub_verify_token'];

if ($verify_token === 'abc123') {
echo $challenge;

$input = json_decode(file_get_contents('php://input'), true);
error_log(print_r($input, true))

Step 5: Test the Integration

  • Go to the Testing tool ( and select the page you made subscription to from the list
  • Hit the create lead button, this will create a test lead for you and send it to your endpoint.
  • At your webhooks endpoint, tail your incoming logs and you should see the test RTU come through

Video Tutorial

Please check our the real time integration video tutorial to see how the integration will look like.


Can we get multiple entries in one RTU?

  • Yes, we batch and send payloads depending on the time of submission for the ad and page. You should engineer your endpoint to decode multiple entries in the incoming payload.

Can we get duplicate RTU for the same lead?

  • Generally, this should not happen, but there might be rare cases when you receive duplicate pings. The way to handle this will be to use the leadgen_id in the incoming ping as the unique key and de-duplicate based on those.

What access token should I use to subscribe the advertiser's page to my app?

  • You should use the page access token of the advertisers page to make the {PAGE_ID}/subscribed_apps call for this purpose. Check the details in section 3 step 3.1

The page access token of the advertisers page has expired. How can I get a long term page access token?

  • When making the call to me/accounts, if the user access token token used is a long lived user access token. Then in that case, the page access tokens returned are long lived and they won’t expire. In order to get long lived user access token see Marketing API Quickstart, Obtain Access Token.

I went through the RTU Integration steps but I am not receiving the pings. What can I do to validate that my subscription is in place?

  • Go to your app at{APP_ID}/webhooks and see if the app is subscribed to receive the leadgen pings by checking for the presence of ‘leadgen’ under fields
  • Go to Graph Explorer and make GET request to {PAGE_ID}/subscribed_apps edge and check to see if your app is amongst those listed in the response. This calls requires page access token of the page you are making the call against.

How can I test the Lead Ads Integration without having to spend on creating a real Lead Ad?

Is there anyway to debug the webhooks integration and understand why it might be failing?