# channels-scope-4 — Facebook Messenger receive arm

> Kickoff: Fable, 2026-07-22 evening. Operator: "What about facebook dm?"
> after a day of multi-channel orders + DECISIONS 8 (FB has DM
> conversations). The simulation surface exists (facebook:dms, gate off);
> this package builds the RECEIVE arm only, mirroring the IG DM pipeline.

## Objective

A real Messenger DM to the Facebook Page produces: webhook event ingested
(not `unknown`-dropped) → contact (`facebook`) + conversation + inbound
message → visible in Inbox with its Facebook chip → shadow trace when the
`facebook:dms` gate is in shadow. NO send arm this package; the gate stays
OFF (not even shadow) until the operator flips it.

## Facts

- The Page webhook `messages` field was subscribed 2026-07-22 (for IG DMs);
  Messenger events ride the same field — check `webhook_events` for already-
  arrived FB messaging events (`entry[].messaging[]` with page recipient).
- IG DM pipeline is the pattern: `ig-dm.service.ts` (contact upsert :82,
  ungated receive), classifier, normalized adapter. Token carries
  `pages_messaging`.
- Inbox/Contacts are multi-channel-ready (channel filter derives from
  `channelsForCase("dm")` — facebook appears automatically once the matrix
  gained facebook:dms).

## Acceptance (gate bars)

1. A simulated Messenger webhook payload (test) and — when one arrives — a
   real one produce contact+conversation+message rows with
   `channel:"facebook"`; nothing classified `unknown`.
2. Inbox shows the conversation with a Facebook chip; filter finds it.
3. No outbound path reachable: gate off; invariant test pins no-send.
4. Per-package suites green; typecheck/build; deploy + health; review
   (step 3) before gate; goldens untouched.

## Out of scope

Send arm, reply UX, gate flip (operator's), commenters-as-contacts.

## Stage log

- 2026-07-22: kickoff written; Seat I (Inbox/Contacts seat) dispatched.
