Integrate GoClaw with Facebook Messenger so people can talk to an agent through a Facebook Page.
This guide follows the Meta App Dashboard as it is in 2026, where an app is built around use cases. If your dashboard still shows the older "Products → Messenger → Settings" layout, the same settings are there under those names.
MESSAGING and MODERATE tasks on the Page)pages_messaging, pages_manage_metadata, pages_show_list) to the app.page_token (step 4).A token generated here does not expire on a timer. It stops working when the person who generated it loses their role on the Page, changes their password, or removes the app — generate a new one then.
app_secret. GoClaw uses it twice: to check the X-Hub-Signature-256 signature on every webhook delivery, and to add appsecret_proof to every Send API call. Without it, anyone who finds the webhook URL can post fake messages, so GoClaw refuses Page comments and Lead Ads submissions until it is set (Messenger messages still pass pairing or the allowlist), and says so once at start-up.my-secret-verify-token-2026, and put it in verify_token. You enter the same string in the dashboard in step 5.If App settings → Advanced → Require App Secret is on, app_secret is required: Meta refuses Send API calls that do not carry appsecret_proof.
config.yamlchannels:
facebook:
enabled: true
page_id: "" # optional Page ID (auto-detected from /me if omitted)
page_token: "${FB_PAGE_TOKEN}" # Page access token (step 2)
verify_token: "${FB_VERIFY_TOKEN}" # the string you chose (step 3)
app_secret: "${FB_APP_SECRET}" # App secret (step 3)
comments_enabled: false # set true to receive Page post comments (webhook field: feed)
auto_reply_comments: false # set true to post the agent's reply as a public comment reply
Keep the values in the environment or the secret vault (${secret:KEY}) rather than in the file.
Start the gateway:
goclaw # no subcommand — running goclaw is what starts the gateway
[facebook] Channel started (webhook: /hook/facebook, comments_enabled: false)
The callback URL is https://<your-domain>/hook/facebook. If gateway.webhook_secret_slug is set, it is https://<your-domain>/hook/<slug>/facebook — use the path the log line prints.
In Messenger API Settings → Configure webhooks:
[facebook] Webhook verified successfully.messages — inbound Messenger chat messages, attachments, and quick-reply payloads.messaging_postbacks — button taps on templates, Get Started, Ice Breakers, and Persistent Menu items.messaging_referrals — Click-to-Messenger ad referrals and m.me?ref=... entry links.feed (under Page subscriptions) — real-time Page post comments when comments_enabled: true.leadgen (under Page subscriptions) — Facebook Lead Ad form submissions.message_echoes) and comments posted by the Page itself are ignored automatically.Then, back in Generate access tokens, click Add subscriptions next to the Page and tick the same fields (messages, messaging_postbacks, messaging_referrals, and optionally feed / leadgen). This step is easy to miss and nothing arrives without it: the webhook settings say what the app wants to hear about, and this says which Page it listens to. The same can be done with the Page token:
curl -X POST "https://graph.facebook.com/v25.0/<PAGE_ID>/subscribed_apps?subscribed_fields=messages,messaging_postbacks,messaging_referrals,feed&access_token=<PAGE_TOKEN>"
curl "https://graph.facebook.com/v25.0/<PAGE_ID>/subscribed_apps?access_token=<PAGE_TOKEN>" # check
With ngrok: run
ngrok http 8080and usehttps://xxxx.ngrok-free.app/hook/facebookas the callback URL. A free ngrok URL changes on every restart, and the webhook has to be registered again.
While the app is in development mode, only people with a role on the app (administrators, developers, testers — App roles → Roles) get replies. Everyone else's messages never reach the webhook.
To answer the public:
pages_messaging (and pages_manage_metadata) under App Review → Permissions and features, with a screencast of the bot answering.facebook_page tool for Page posts, comments, or analytics, also grant these permissions to the Page token:pages_manage_posts — publish, schedule, update, and delete Page posts (create_post, update_post, delete_post).pages_read_engagement & pages_manage_engagement — read comments, post public comment replies, send Comment-to-Messenger Private Replies (private_reply), like objects, and hide/delete comments (moderate_comment).read_insights — read Page and post analytics (page_insights, post_insights: Views, Reach, Engagements, Reactions).bindings:
- agent_id: "support"
match:
channel: "facebook"
facebook_page Agent Tool (Posts, Comments, Analytics & Flows)When channels.facebook.page_token is configured, agents can call the facebook_page tool:
page_info, list_posts (published or scheduled: true), get_post, create_post (text, link, remote image_url, or workspace image_path, with optional scheduled_publish_time), update_post, delete_post.list_comments, reply_comment (public reply under a comment), private_reply (sends a direct Messenger DM to the commenter via recipient.comment_id within 7 days of their comment), moderate_comment (hide, unhide, or delete), like_object.page_insights (page_media_view, page_impressions_unique, page_post_engagements, page_daily_follows_unique, page_video_views by day, week, or days_28) and post_insights (post_media_view, post_impressions_unique, post_clicks, post_reactions_by_type_total).send_quick_replies (up to 13 quick-reply chips), send_template (button template or generic carousel of up to 10 cards), get_messenger_profile, and set_messenger_profile (Welcome Greeting, Get Started payload, Ice Breakers FAQ starters, and Persistent Menu).Administrative mutations (create_post, update_post, delete_post, moderate_comment, page_insights, post_insights, set_messenger_profile) require an admin or SOP context so external chat users cannot trigger them via prompt injection.
/me/messages, Graph API v25.0) with messaging_type: RESPONSE. Meta allows that within 24 hours of the person's last message; after that a reply is refused, and GoClaw does not send message tags.comments_enabled: true), the agent can reply publicly (reply_comment or auto_reply_comments: true) and send a private DM (private_reply) within 7 days of the comment.first_name, last_name) are resolved from the Graph API and cached in memory.dm_policychannels.dm_policy and pairing are for people who write to the Page in Messenger. A comment under a Page post and a Lead Ads submission are events from the Page, not a person asking to talk to the agent, so they do not go through pairing or the allowlist:
| Event | Reaches the agent when | Runs as |
|---|---|---|
| Messenger message, postback, referral | dm_policy lets the sender in (see Pairing) |
the paired user, with their role |
Page post comment (feed) |
app_secret is set and comments_enabled: true, under any dm_policy |
an ordinary user, with no paired identity |
Lead Ads submission (leadgen) |
app_secret is set and the Page is subscribed to leadgen, under any dm_policy |
an ordinary user, with no paired identity |
comments_enabled: false, comments are not handled at all, whatever dm_policy is.app_secret, comments and leads are not handled at all: they skip the access gate, and only the signature proves that Facebook sent them.roles: [user] apply to comments and leads as they do to paired users. To write an instruction for comments or leads alone, give it channels: [facebook] and a message_match on Facebook Page Comment or Facebook Lead Ad Submission, the first line of what the agent receives for each.From an account with a role on the app (step 6), send a message to the Page. The log shows [facebook] Message from <PSID>: ….
With the default channels.dm_policy: pairing, the first reply to a Messenger message is Pairing Required with an 8-character code and the sender's Facebook ID (their page-scoped ID, PSID). Approve the code on the Web UI's Pairing tab — see Pairing — and send the message again; this time the agent's reply arrives in Messenger.
| Symptom | Cause and fix |
|---|---|
| "The URL couldn't be validated" when saving the webhook | GoClaw is not reachable over HTTPS at that URL, the path is wrong (check the slug), or verify_token differs. The log says Webhook verification failed when the token is the problem. |
| Webhook verified, but no message ever arrives | The Page is not subscribed: Add subscriptions in step 5. Or the sender has no role on an app in development mode (step 6). |
Invalid signature in the log |
app_secret is not this app's secret. |
Comments or leads never reach the agent, and the start-up log says Page events (comments and lead ads) are refused |
app_secret is not set (step 3). |
Send fails with error 190 (OAuthException) |
The Page token is no longer valid — generate a new one (step 2). |
| Send fails with "API calls from the server require an appsecret_proof argument" | Require App Secret is on and app_secret is not set. |
| Send fails with error 10 / 2018278 | The reply came more than 24 hours after the person's last message. |
| Send fails with a permissions error (code 200) | The token lacks pages_messaging, or the app has no Advanced access and the person has no role on it. |
| Messages stop after the server was down | Meta retries for a while; after about an hour of failures it disables the webhook for the Page and sends a "Webhooks Disabled" alert. Fix the endpoint, then subscribe again (step 5). |