How a Meta lead becomes a CRM lead
The full trace — from a Facebook lead form submission to a lead row in the CRM, assigned and notified, with the real services and jobs involved.
How a Meta lead becomes a CRM lead
Someone fills in your Facebook lead form. Seconds later, a lead card sits in your CRM with an owner, a follow-up task, and a notification. This page follows that journey end to end — the happy path first, then the real names, then how to debug it when a lead goes missing.
The journey in plain language
- A person submits your Meta Instant Form on Facebook or Instagram.
- Meta calls your workspace's webhook — a private address unique to your workspace and connection.
- The CRM verifies the call really came from Meta (digital signature), then queues it.
- A background worker checks the form against your allowlist — only forms you ticked in Lead forms & mapping are accepted.
- The CRM fetches the full lead from Meta (form answers, ad id, created time).
- Answers are mapped to CRM fields, checked for duplicates, and written as a Lead.
- The lead's creation triggers the rest of the chain: scoring, activity log, automations, assignment, notifications.
sequenceDiagram
autonumber
participant P as Person (Facebook form)
participant M as Meta
participant W as CRM webhook
participant Q as Queue worker
participant C as MetaConnector
participant L as Lead pipeline (CRM)
participant U as Assignee
P->>M: Submits lead form
M->>W: POST /api/webhook/{tenant}/{source}/{token}
W->>W: Verify X-Hub-Signature-256
W->>Q: ProcessInboundWebhook job (queue: webhooks)
Q->>C: handleWebhook()
C->>C: Allowlist check (settings.lead_forms.form_ids)
C->>M: GET /{lead_id}?fields=field_data,created_time,ad_id,form_id
M-->>C: Lead data
C->>L: Map fields + dedupe + create Lead
L->>L: Scoring, activity, automations (lead_created)
L->>U: Assignment + database notification (+ push)
The technical trace
- Submission. The person completes a form attached to an ad, on your selected Page.
- Webhook call. Meta delivers
entry[].changes[]withfield = leadgentoPOST /api/webhook/{tenant}/{source}/{token}, handled byInboundWebhookController. The raw body'sX-Hub-Signature-256header is validated as an HMAC of the payload using the app secret (skipped only when no secret is configured — a misconfiguration worth fixing). - Queued processing. The controller stores the webhook log and dispatches
App\Jobs\ProcessInboundWebhookto thewebhooksqueue (3 tries, 30-second backoff; the log records success or failure). - Allowlist gate.
App\Services\LeadSources\MetaConnector::handleWebhook()extracts theleadgen_idand checks itsform_idagainstsettings.lead_forms.form_idson thelead_source_connectionsrow. Unselected forms are skipped by design and recorded with "Skipped: lead form {id} is not one of the forms selected…". - Graph fetch. The lead is pulled from the Graph API:
GET /{lead_id}?fields=field_data,created_time,ad_id,form_id(default versionv22.0, configurable).field_datais the raw question/answer array. - Mapping + creation. The confirmed field mapping converts answers into CRM fields;
createMappedLead()writes the Lead (sourcemeta, plus the ad id for attribution).App\Services\LeadSources\LeadIngestionServiceapplies duplicate detection using thelead_duplicatestable before/while creating. - Lifecycle side effects.
App\Observers\LeadObserver::created()runs the full chain: lead scoring (LeadScoringService), activity logging (LeadActivityService), automations (AutomationDispatcher::dispatch('lead_created')— your follow-up task), realtime delivery (RealtimeDelivery), theLeadReceivedNotification, and theLeadAssignedevent. - Assignment + notification. If the lead arrived without an owner,
LeadAutoAssignerappliessettings.leads.assignment(round robin / specific / off). The assignee gets a database notification — whichPushDatabaseNotificationsturns into a push if FCM is configured (How push notifications work).
Timing On a production install with a queue worker, the lead normally appears within seconds. On the local XAMPP install, QUEUE_CONNECTION=sync executes the job inline during the request — still immediate, but without a worker there is nothing to retry on failure.
Troubleshooting notes
| Symptom | Likely cause | Where to look |
|---|---|---|
| No lead at all, webhook log empty | Meta webhook not subscribed to the Page's leadgen events, or wrong callback URL |
Meta app configuration vs Meta Developer settings |
| Webhook arrives but signature fails | App secret mismatch between Meta and the platform | Platform Meta credentials; the log records the rejection |
| Log shows "Skipped: lead form …" | The form is not ticked in your allowlist | Lead forms & mapping |
| Log shows success but no lead | Duplicate detection or mapping produced an empty lead | lead_duplicates table; check the form mapping |
| Leads arrive instantly one day, not the next | Queue worker stopped (production) or token expired (needs_reauth) |
Worker/cron health; connection health and Meta won't connect |
| Lead exists but nobody was notified | Routing is Unassigned or the assignee has no notification access | Lead assignment & follow-ups |
Untick a form to go quiet — not to delete history The allowlist controls arrival, not existing leads. Leads already imported stay in the CRM, and unticking a form skips only future arrivals from it.
Related guides
Was this page helpful?
Thanks — noted. ✦