Ads Manager says 47 leads. Your CRM has 12. Nothing in the logs looks broken.
This is one of the most common failures we see, and in our experience it is nearly always one of five causes. Here they are roughly in order of how often they turn out to be the culprit.
If you have not built the connection yet and landed here while planning it, start with how to connect Meta ads to your CRM instead, which covers the four routes and what each one costs you.
Why are my Meta lead ads not syncing to my CRM?
Because one link in the chain failed silently. Meta does not warn you when delivery stops, so the integration keeps looking configured while nothing arrives. In our experience it is one of these five, in roughly this order of frequency.
1. The page subscription was never actually created
Connecting your ad account is not the same as subscribing to a page's leads. Lead notifications are delivered per page, and a page that is not subscribed generates leads that go nowhere.
The tell: leads exist in Ads Manager and in the form's own CSV export, but your webhook endpoint has no record of the request arriving at all. Nothing to debug in your code, because your code was never called.
Check the subscription on the page itself, not the ad account, and confirm the leadgen field is subscribed.
2. The access token expired
Short-lived tokens expire in about an hour. Long-lived page tokens last around 60 days. A token also dies when the person who authorised it changes their password, loses their page role, or leaves the company.
That last one is brutal because it is invisible. The marketing contractor who connected the account in March is offboarded in June, and lead sync stops on a Tuesday with no deploy and no error in your application.
Watch out: Alert on the absence of leads, not just on errors. A silent integration failure produces no exception, no 500, no log line. It produces nothing at all, which is exactly what a quiet afternoon looks like. If a page normally sees leads daily and has seen zero for 24 hours, something should page you.
3. Your webhook returned non-200 and Meta gave up
Meta expects a fast 200. If your endpoint is slow, throws, or returns anything else, Meta retries for a while and then stops sending. If your handler does the real work (database writes, WhatsApp sends, downstream API calls) before responding, you are one slow dependency away from timing out and losing the lead permanently.
Acknowledge first, work second. Return 200 the moment you have the payload safely persisted, then process it out of band.
4. The form fields changed
Someone duplicates a lead form, renames "Phone Number" to "Contact Number", and your field mapping silently maps nothing. The lead arrives, your parser looks for a key that no longer exists, and you write a row with a name and no way to contact them.
Map defensively and log the raw payload. When a field goes missing you want the original JSON, not just your parsed interpretation of it.
5. Phone number formatting, the one nobody checks
This is the subtle one, and it is our favourite because it fails so quietly.
You store numbers in E.164, because that is correct:
+91 79898 85509
Meta's webhook sends the same number as bare digits:
917989885509
Those are the same human being and two completely different strings. Any exact-match lookup returns zero rows.
The fix is to compare on a normalised suffix rather than the formatted string. Strip everything that is not a digit and match the last ten:
WHERE RIGHT(REGEXP_REPLACE(phone, '[^0-9]', ''), 10) = ?
Ten digits is the right window for Indian numbers: it survives +91, 0091, a leading 0, spaces, and dashes, while staying specific enough not to collide in practice.
Normalise on the way in as well. Every inbound path, ad webhook, WhatsApp webhook, CSV import, manual entry, should write the same canonical format. Matching is a last line of defence, not a substitute for storing numbers consistently.
The pattern underneath all five
Four of these five fail silently. No error, no exception, no red dashboard. The system does not tell you it is broken; it just quietly stops being useful, and you find out when a client asks why nobody called them back.
If you build this yourself, spend your effort on detection: alert on the absence of expected leads, log raw payloads before parsing, and treat a quiet integration as a suspicious one.
Or skip the archaeology. Kredoo does Meta and Google Ads lead capture, phone normalisation, and WhatsApp follow-up as one path that we keep working.
Questions people ask
Why are my Meta lead ads not syncing to my CRM?
Almost always one of five causes: the Page subscription was never created even though the app subscription exists, the access token expired, your webhook returned a non-200 response and Meta stopped retrying, a form field was renamed or removed, or the phone number format was rejected by your CRM. None of these produce an error you will see, which is why the integration looks healthy while nothing arrives.
Why do some Meta leads reach my CRM and others do not?
Field formatting, and phone numbers specifically. Meta returns numbers in a shape your CRM may reject, and many systems fail the whole record when one field is invalid instead of skipping that field. If delivery is intermittent rather than absent, check formatting before you check the connection.
Does Meta retry a failed lead webhook?
Only for a limited period, and only if you did not already return a 200. Once your endpoint answers 200, Meta considers the lead delivered and will never send it again, even if your code crashed immediately afterwards. Store the lead before you acknowledge.
How do I know if my Meta Page subscription is active?
Check the Page subscription, not just the app subscription. GET /{page-id}/subscribed_apps should return your app. An active app-level subscription with no Page-level subscription is the most common false positive, because every dashboard check passes and no lead is ever delivered.
Why did my Meta lead ads stop working suddenly?
An expired token is the usual cause of a working integration going quiet with no other change. Short-lived user tokens expire within about 60 days, and a Page token derived from one dies with it. A System User token does not expire, which is why production integrations should use one.
Stop losing leads to the follow-up gap
Kredoo captures leads from Meta and Google Ads, then follows up on WhatsApp automatically. Live in 5 minutes.
Get Started