Facebook Lead Ads · AsyncAPI Specification

Facebook Lead Ads Webhooks

Version

View Spec View on GitHub AdvertisingLead GenerationLead AdsMarketing APIFacebookInstagramMetaWebhookAsyncAPIWebhooksEvents

AsyncAPI Specification

Raw ↑
generated: '2026-08-14'
method: searched
source: https://developers.facebook.com/docs/graph-api/webhooks/getting-started
also:
  - https://developers.facebook.com/documentation/ads-commerce/marketing-api/guides/lead-ads/quickstart/webhooks-integration
  - https://developers.facebook.com/docs/marketing-api/guides/lead-ads/retrieving
spec_type: Webhooks
asyncapi_published: false
asyncapi_note: >-
  Meta publishes NO AsyncAPI document for the Graph API webhook surface. Searched
  developers.facebook.com, the site llms.txt index, and the graph.facebook.com host
  (/asyncapi.yaml, /asyncapi.json) — nothing. The event surface is real and well documented
  in prose, so it is captured below as a webhook catalog rather than fabricated as an
  AsyncAPI document.
note: >-
  Real-time lead delivery is the headline feature of Facebook Lead Ads — webhooks are how a
  CRM receives a lead the moment it is submitted, instead of polling /{form-id}/leads. The
  event surface is a single field (`leadgen`) on a single object type (`page`), which makes
  this one of the narrowest webhook catalogs in the catalog and one of the most heavily used.
transport: HTTPS POST to a developer-registered callback URL
subscription:
  model: app subscribes to an object+field, then each Page subscribes the app
  operations:
    - operationId: subscribeAppWebhook
      spec: openapi/facebook-lead-ads-subscriptions-api-openapi.yml
      path: /{app-id}/subscriptions
      description: >-
        Register the app's callback URL and verify token for object=page, fields including
        "leadgen".
    - operationId: pageSubscribedApps
      spec: openapi/facebook-lead-ads-subscribed-apps-api-openapi.yml
      path: /{page-id}/subscribed_apps
      description: Subscribe the app to a specific Page with subscribed_fields including "leadgen".
  required_permissions:
    - pages_manage_metadata
    - pages_show_list
    - leads_retrieval
verification_handshake:
  method: GET
  query_parameters:
    - name: hub.mode
      value: subscribe
      description: Always the literal string "subscribe".
    - name: hub.challenge
      type: int
      description: '"An int you must pass back to us."'
    - name: hub.verify_token
      type: string
      description: >-
        "A string that we grab from the Verify Token field in your app's App Dashboard."
        Compare it to your configured value before echoing the challenge.
  expected_response: Echo hub.challenge back in the response body.
security:
  signature_header: X-Hub-Signature-256
  algorithm: HMAC-SHA256
  key: the app secret
  format: 'sha256=<hex digest of the raw request body>'
  validation: >-
    "Generate a SHA256 signature using the payload and your app's App Secret. Compare your
    signature to the signature in the X-Hub-Signature-256 header (everything after
    sha256=). If the signatures match, the payload is genuine."
  note: >-
    The digest must be computed over the RAW body. Re-serialising the parsed JSON changes
    the bytes and breaks verification — the most common integration failure on this surface.
delivery:
  expected_response: 200 OK over HTTPS
  retry_policy: >-
    "Retry immediately, then try a few more times with decreasing frequency over the next
    36 hours."
  drop_after: 36 hours
  ordering: not guaranteed
  at_least_once: true
  agent_note: >-
    Delivery is at-least-once with no idempotency key, so a consumer MUST de-duplicate on
    the leadgen_id. See conventions/facebook-lead-ads-conventions.yml — this API publishes
    no idempotency contract at all.
channels:
  - name: page/leadgen
    object: page
    field: leadgen
    description: >-
      Fired when a user submits a lead generation form on a Facebook or Instagram lead ad.
    payload_fields:
      - {name: object, description: 'The object type — "page".'}
      - {name: entry, description: Array of change batches, one per Page.}
      - {name: 'entry[].id', description: The Page ID.}
      - {name: 'entry[].time', description: Unix timestamp of the batch.}
      - {name: 'entry[].changes', description: Array of change objects.}
      - {name: 'entry[].changes[].field', description: 'The subscribed field — "leadgen".'}
      - {name: 'entry[].changes[].value.leadgen_id', description: The ID of the lead just created.}
      - {name: 'entry[].changes[].value.page_id', description: The Page that owns the form.}
      - {name: 'entry[].changes[].value.form_id', description: The lead generation form.}
      - {name: 'entry[].changes[].value.adgroup_id', description: The ad that produced the lead.}
      - {name: 'entry[].changes[].value.ad_id', description: The ad ID.}
      - {name: 'entry[].changes[].value.created_time', description: Unix timestamp of submission.}
    follow_up:
      description: >-
        The notification carries IDs, not lead content. Read the lead body with getLead
        against /{lead-id}.
      operationId: getLead
      spec: openapi/facebook-lead-ads-meta-marketing-api-lead-ads-api-openapi.yml
    rate_limit:
      bucket: LeadGen business use case
      formula: Calls within 24 hours = 4800 * Leads Generated
      error_code: 80005
      detail: rate-limits/facebook-lead-ads-rate-limits.yml
alternative_to_webhooks:
  - method: bulk read by form
    operationId: listLeadsForForm
    path: /{form-id}/leads
  - method: bulk read by ad
    operationId: listLeadsForAd
    path: /{ad-id}/leads
  - method: CSV export
    url: https://www.facebook.com/ads/lead_gen/export_csv/
    params: [from_date, to_date]
    note: POSIX/UNIX timestamp formats.
summary:
  channels: 1
  asyncapi_document: false
  signature_verification: true
  replay_protection: false