Facebook Messenger
The Messenger channel connects your agent to a Facebook Page’s Messenger inbox. Meta POSTs each message to a webhook the adapter serves, and replies go out through the Send API. It is provided by the external @mono-agent/messenger-adapter package and loaded through channels.plugins[]. Coverage: config — see feature-registry row messenger.graph.
Quick start
Section titled “Quick start”- In the Meta developer console create an app with the Messenger product, connect your Page, and generate a Page access token. Copy the App secret from the app’s basic settings. Choose any string as the verify token.
- Put the three values in the agent’s
.env:
MONO_AGENT_MESSENGER_PAGE_ACCESS_TOKEN=...MONO_AGENT_MESSENGER_APP_SECRET=...MONO_AGENT_MESSENGER_VERIFY_TOKEN=...- Declare the plugin and allow one or more users:
{ "channels": { "plugins": [ { "package": "@mono-agent/messenger-adapter", "id": "messenger", "config": { "enabled": true, "allowedUserIds": ["1234567890123456"], "host": "0.0.0.0", "port": 8650, "allowNonLoopback": true } } ] }}- Expose
http://<host>:8650/messenger/webhookover HTTPS (a reverse proxy or a tunnel such as cloudflared) and register that public URL in the app’s Messenger Webhooks settings with the same verify token, subscribed tomessagesandmessaging_postbacks. Meta callsGETwithhub.challengeduring registration; the adapter answers it once the agent is running.
Configuration
Section titled “Configuration”| Key | Type | Default | Purpose |
|---|---|---|---|
config.enabled | boolean | false | Opt-in switch. Off means the channel reports “disabled” (not “waiting”). |
config.allowedUserIds | string[] | [] | Page-scoped user ids (PSIDs) allowed to talk to the agent. |
config.allowAllUsers | boolean | false | When true, every user is allowed; the allowlist is ignored. |
config.host | string | 127.0.0.1 | Bind address for the webhook server. |
config.port | integer | 8650 | Bind port. |
config.webhookPath | string | /messenger/webhook | Webhook route. <path>/health answers liveness checks. |
config.apiVersion | string | v21.0 | Graph API version used for sends. |
config.allowNonLoopback | boolean | false | Required to bind anything other than loopback. |
config.proactiveMessagingType | RESPONSE | UPDATE | MESSAGE_TAG | RESPONSE | Send API messaging_type for proactive cron/webhook deliveries. |
config.proactiveTag | string | — | Policy tag required with MESSAGE_TAG, e.g. CONFIRMED_EVENT_UPDATE. |
Secrets are read only from MONO_AGENT_MESSENGER_PAGE_ACCESS_TOKEN, MONO_AGENT_MESSENGER_APP_SECRET, and MONO_AGENT_MESSENGER_VERIFY_TOKEN — they have no JSON key. Every other field also has a MONO_AGENT_MESSENGER_* env override (see Environment variables).
Finding a PSID
Section titled “Finding a PSID”A PSID is specific to your Page. Temporarily set allowAllUsers: true, send the Page a message, read the id from the start log’s unauthorized/handled lines or the run artifacts, then move it into allowedUserIds and turn allowAllUsers off. An unlisted sender receives a one-line denial; their text never reaches the agent.
Conversation ids and proactive delivery
Section titled “Conversation ids and proactive delivery”Conversations are keyed messenger:<psid>. Cron jobs and webhook endpoints deliver to one with notifyConversationId: "messenger:<psid>": the final answer is posted verbatim as plain text, split into 2,000-character messages, and recorded to that conversation’s history so a later reply resumes with it in context. See Delivery & send tools.
Meta only delivers ordinary messages within 24 hours of the user’s last message. A reminder that may fire outside that window needs proactiveMessagingType: "MESSAGE_TAG" and a policy-compliant proactiveTag.
Behaviour
Section titled “Behaviour”- Webhook POSTs are verified with
X-Hub-Signature-256over the raw body, acknowledged immediately, and processed afterwards; duplicate deliveries are dropped by message id. - Messages from one user run in order; up to four queue behind an active turn, then the user gets a short busy reply.
/cancelaborts the active turn and retires anything already queued behind it, so a withdrawn prompt never answers later; a message sent after the cancel runs normally./helpand/startanswer without a model call. - Images and PDF/text files are downloaded from Meta’s CDN and passed as attachments. Downloads are restricted to HTTPS on
fbcdn.net/fbsbx.com; redirects are followed manually with every hop re-checked against that policy, hostnames must resolve entirely to public addresses, and the 20 MiB cap is enforced while streaming (an absent or dishonestContent-Lengthcannot exhaust memory). Audio, video, locations, and other files are described in the request text. - Replies are never silently duplicated: Meta’s Send API has no idempotency key, so a message POST whose outcome is unknown (timeout, transport failure, 5xx) is reported as an ambiguous delivery instead of being replayed. Only a
429, which the server refused before acting, is retried. - Replies are plain text: Markdown is flattened before sending.
Related
Section titled “Related”The Telegram personal-assistant playbook translates directly: add the @mono-agent/messenger-adapter entry under channels.plugins[] and point cron notifyConversationId values at messenger:<psid>. See the Channels overview for the shared allowlist and status model.