# Meta forms (Lead Ads)

When someone fills out a lead form on one of your Pages, Meteor stores the lead, creates or updates the contact in the CRM and notifies you with the **`lead.received`** event. You do not need to connect to Meta: the lead reaches you already normalized.

> Scope `contacts:read`, for the event and for reading the forms. Connecting Pages and creating or archiving forms is done from Automations → Forms in the panel.

## Receiving the leads

Subscribe your outbound events endpoint to `lead.received` (see [Webhooks](webhooks.html)). If you already have one, add the event to it:

```ts
import { Met } from '@meteor.ia/sdk';

const met = new Met(process.env.MET_KEY!, { workspaceId: 42 });
const [ep] = await met.webhooks.subscriptions.list();
await met.webhooks.subscriptions.update(ep.id, {
  enabled_events: [...ep.enabled_events, 'lead.received'],
});
```

Each event carries:

```jsonc
{
  "type": "lead.received",
  "data": {
    "lead": {
      "id": "8c1f…",                 // the lead in Meteor
      "leadgen_id": "1234567890",    // the lead's id in Meta
      "form_id": "998877",
      "form_name": "Cotiza tu plan",
      "page_id": "556677",
      "platform": "ig",              // fb or ig
      "is_organic": false,
      "ad_id": "…", "ad_name": "…",
      "campaign_id": "…", "campaign_name": "…",
      "created_at": "2026-10-20T15:04:05+0000"
    },
    "answers": [
      { "key": "full_name", "label": "Nombre completo", "value": "Ana Pérez" },
      { "key": "email", "label": "Correo", "value": "ana@correo.co" }
    ],
    "contact_data": { "name": "Ana Pérez", "email": "ana@correo.co", "phone": "+573001234567" },
    "contact": { "id": 4512, "action": "created" }
  }
}
```

- `answers` follows the order of the form's questions, with each one's label. A multiple-choice answer arrives joined with commas.
- `contact_data` is the contact information recognized in the answers; a field the form did not ask for arrives as `null`.
- `contact.action` is `created` if the contact is new and `updated` if it already existed. If the lead carries neither a phone nor an email, `contact` is `null`.

It is sent **once per new lead that arrives live**. Leads brought in when syncing a form (leads from previous days) do not emit it.

## Looking up the forms

```ts
for await (const form of met.leadAds.iterateForms()) {
  console.log(form.id, form.name, form.page.connected, form.questions.map((q) => q.key));
}

const detail = await met.leadAds.retrieveForm('998877');
console.log(detail.leads_in_met);
```

`page.connected: false` means the Page was disconnected: its new leads no longer arrive, although the stored ones are kept.
