Zodiac Tech CRM Docs

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.

everyone Medium ⏱ 10 min Updated 2026-10-05

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

  1. A person submits your Meta Instant Form on Facebook or Instagram.
  2. Meta calls your workspace's webhook — a private address unique to your workspace and connection.
  3. The CRM verifies the call really came from Meta (digital signature), then queues it.
  4. A background worker checks the form against your allowlist — only forms you ticked in Lead forms & mapping are accepted.
  5. The CRM fetches the full lead from Meta (form answers, ad id, created time).
  6. Answers are mapped to CRM fields, checked for duplicates, and written as a Lead.
  7. 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

  1. Submission. The person completes a form attached to an ad, on your selected Page.
  2. Webhook call. Meta delivers entry[].changes[] with field = leadgen to POST /api/webhook/{tenant}/{source}/{token}, handled by InboundWebhookController. The raw body's X-Hub-Signature-256 header is validated as an HMAC of the payload using the app secret (skipped only when no secret is configured — a misconfiguration worth fixing).
  3. Queued processing. The controller stores the webhook log and dispatches App\Jobs\ProcessInboundWebhook to the webhooks queue (3 tries, 30-second backoff; the log records success or failure).
  4. Allowlist gate. App\Services\LeadSources\MetaConnector::handleWebhook() extracts the leadgen_id and checks its form_id against settings.lead_forms.form_ids on the lead_source_connections row. Unselected forms are skipped by design and recorded with "Skipped: lead form {id} is not one of the forms selected…".
  5. Graph fetch. The lead is pulled from the Graph API: GET /{lead_id}?fields=field_data,created_time,ad_id,form_id (default version v22.0, configurable). field_data is the raw question/answer array.
  6. Mapping + creation. The confirmed field mapping converts answers into CRM fields; createMappedLead() writes the Lead (source meta, plus the ad id for attribution). App\Services\LeadSources\LeadIngestionService applies duplicate detection using the lead_duplicates table before/while creating.
  7. 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), the LeadReceivedNotification, and the LeadAssigned event.
  8. Assignment + notification. If the lead arrived without an owner, LeadAutoAssigner applies settings.leads.assignment (round robin / specific / off). The assignee gets a database notification — which PushDatabaseNotifications turns 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.

Was this page helpful?