# Meta Lead Ads — Post-Configuration Runbook

## Context

The Meta Lead Ads → CRM pipeline (migrations, models, `MetaWebhookController`,
`MetaConnectionController`, `ProcessMetaLead` job, `MetaApiService`, `MetaLeadService`) is already
built and merged — see the "Meta (Facebook/Instagram) Lead Ads Ingestion" section of
`docs/business-rules.md` and the "Meta (Facebook/Instagram) Lead Ads" table in `docs/database.md`
for what exists today. This file is an operational runbook for going live with real Meta
credentials — not a build plan. No code changes are implied by it.

Two things are still stubs by design: the Graph API-backed `/api/meta/forms`,
`/api/meta/forms/fields`, and OAuth (`/meta/connect`, `/meta/callback`) endpoints return
placeholders/501 until wired to real Meta credentials. The steps below route around that by using
the Meta Graph API Explorer / Ads Manager UI directly for the parts this codebase doesn't yet
automate, and call this codebase's real endpoints for the parts it does.

## Steps

1. **Create a Meta App** at developers.facebook.com (type: Business), note the **App ID** and
   **App Secret**. Add the "Webhooks" and "Facebook Login for Business" products.

2. **Register the connection in the CRM** — `POST /api/meta/connect` (auth:sanctum, permission
   `setting.meta_credential.update`) with `app_id`, `app_secret`, and a `verify_token` you invent
   yourself (any random string — it's not a Meta-issued value, just a shared secret for the webhook
   handshake). This activates the single `meta_connections` row
   (`App\Http\Controllers\Meta\MetaConnectionController::connect`).

3. **Subscribe the App's webhook to `leadgen`** in the Meta App dashboard (Webhooks product →
   Page object → subscribe to `leadgen`), pointing the callback URL at
   `https://<your-domain>/api/webhook/meta/lead` and using the same `verify_token` from step 2.
   Meta will immediately call `GET /api/webhook/meta/lead` with a `hub.challenge` —
   `MetaWebhookController::verify` checks it against the active connection's `verify_token`.

4. **Get a Page Access Token** — via Graph API Explorer or Facebook Login for Business, obtain a
   User access token with `pages_show_list`, `pages_manage_ads`, `leads_retrieval`, then call
   `GET /me/accounts` to list Pages and their long-lived Page access tokens. (This app's
   `/api/meta/pages`/connect OAuth flow is a route stub today — `MetaAuthController` returns 501 —
   so this step is manual until that's built out.)

5. **Insert the Page into the CRM** — no endpoint exists yet to auto-populate `meta_pages` from step
   4's data, so create the row directly (e.g. `php artisan tinker`):
   ```php
   \App\Models\MetaPage::create([
       'meta_connection_id' => \App\Models\MetaConnection::first()->id,
       'page_id' => '<page id from step 4>',
       'page_name' => '<page name>',
       'page_access_token' => '<page access token from step 4>',
       'status' => 'active',
   ]);
   ```

6. **Subscribe the Page itself to the App's webhook** — `POST /{page-id}/subscribed_apps` with
   `subscribed_fields=leadgen`, authenticated with the Page access token from step 4. (A Page-level
   subscription is required in addition to the App-level webhook from step 3 — Meta won't deliver
   `leadgen` events for a Page that hasn't explicitly subscribed.)

7. **Find the Lead Ad Form's field names** — via Graph API Explorer, `GET /{form-id}` or
   `GET /{form-id}/leads` on a real or test lead, to see the exact `field_data[].name` values your
   form uses (e.g. `full_name`, `email`, `phone_number`, or custom question keys).

8. **Configure field mapping** — `POST /api/meta/forms/mapping` with `form` (the form_id from step
   7) and `mapping: [{source_field, crm_field}, ...]`. If skipped, `MetaLeadService` falls back to
   the common defaults `full_name→name`, `email→email`, `phone_number→phone` — fine for a standard
   form, insufficient for custom questions.

9. **Ensure a queue worker is running** for the `meta` queue — `ProcessMetaLead` is dispatched
   there, not processed inline (`php artisan queue:work --queue=meta` or whatever supervisor config
   already runs the app's other queues, per `docs/architecture.md`'s Queue section).

10. **Test end-to-end**:
    - Easiest: in Meta Ads Manager, open the Lead Form and use its built-in "Create Test Lead"
      button — this fires a real webhook through the full pipeline built in this codebase.
    - Or, without touching Meta at all: `php artisan meta:fake-webhook` (already built) exercises
      the same webhook → job → Graph API → Lead pipeline with mocked HTTP, useful to confirm the
      Page/mapping config above is wired correctly before testing against real Meta traffic.
    - Check `ad_webhook_logs` (delivery log) and `ad_leads` (per-lead processing status/error) for
      results; a created `Lead` should appear with `source = 'Meta'`.

## Not required, but worth flagging

`MetaConnectionController::forms()` / `formFields()` and `MetaAuthController` are intentionally
still placeholders (steps 4 and 7 above route around them manually). If this becomes a recurring
setup (multiple Pages/forms, or repeated for future clients), it's worth building those out for
real — a distinct, larger task, not part of this runbook.
