Meta Marketing API webhook receiver — "Create & manage ads" use case.
GET → Meta verification challenge (hub.mode=subscribe).
POST → ad-account / campaign / adset / ad / async-request events.
Subscribed object: `application` (selected in the Meta App Dashboard under
Use cases → Create & manage ads → Webhooks → Select product: Application).
Common subscribed fields:
- ad_account — settings, spend cap, status changes
- adcampaign_status — campaign lifecycle changes
- adset_status — ad set lifecycle changes
- ad_status — ad lifecycle changes
- async_request_completed
- async_session_completed
- adimage
Signature is verified with X-Hub-Signature-256 against FACEBOOK_APP_SECRET.
The Page/IG webhook lives separately at /app/api/webhooks/facebook because
the payload shapes and tenant-resolution paths are different.
Tenant resolution: Marketing API webhooks identify themselves by ad-account
id buried inside `change.value`. Multi-tenant routing requires a lookup
table (e.g. `meta_ad_accounts: ad_account_id → organization_id`); until
that exists, events are logged and routed to the org named by
META_DEFAULT_ORGANIZATION_ID (single-tenant fallback). Returning 200 even
when the tenant is unknown is intentional — Meta retries 5xx aggressively.