Install
openclaw skills install meta-fb-inboxCheck Facebook page inbox messages via Meta Business Suite browser automation. Use when asked to check Facebook messages, reply to FB customers, or manage Facebook page inbox. Supports multiple pages with custom aliases.
openclaw skills install meta-fb-inbox⚠️ CRITICAL: Always use profile:"openclaw" (isolated browser) for all browser actions. Never use Chrome relay. Keep the same targetId across operations to avoid losing the tab.
⚠️ CONTEXT Management:
snapshot refs:"aria" compact:true to find element refs, then act + click/type with ref.Before first use, check if config.json exists:
read file_path:"skills/meta-fb-inbox/config.json"
⚠️ PATH RULE: Always use workspace-relative paths starting from skills/meta-fb-inbox/. Never use ../ or absolute paths.
If missing or empty, help the user run the setup wizard:
cd skills/meta-fb-inbox
node scripts/setup.js
config.json contains a pages array. Each page has an alias (display name) and url (Meta Business Suite inbox URL):
{
"pages": [
{
"alias": "fb fanpage",
"url": "https://business.facebook.com/latest/inbox/all/?&asset_id=123456789012345"
},
{
"alias": "fb fanpage 2",
"url": "https://business.facebook.com/latest/inbox/all/?&asset_id=987654321098765"
}
]
}
When the user asks to check messages:
config.json → pages.This is the first step for all operations below.
Check and start browser service:
browser action:"status"
If "running": false, start the browser service:
browser action:"start" profile:"openclaw"
Wait 2-3 seconds for the service to initialize.
Read config to get the target page URL:
read file_path:"skills/meta-fb-inbox/config.json"
Resolve the page alias to a URL (see "Resolving a Page" above).
Open browser:
browser action:"open" profile:"openclaw" targetUrl:"<pageUrl>"
Capture targetId and url from response.
Wait for page load:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":3000}
Check login state:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"evaluate","fn":"function(){return window.location.href;}"}
business.facebook.com and shows the inbox → logged in, proceed.Proceed with the user's request.
Clean Up Browser Tabs (after all operations):
a. Open a blank tab:
browser action:"open" profile:"openclaw" targetUrl:"about:blank"
b. Get all tabs:
browser action:"tabs" profile:"openclaw"
c. Close all tabs except the newest one (about:blank):
browser action:"close" profile:"openclaw" targetId:"<your_targetId>"
Complete flow: open page → screenshot → report.
read file_path:"skills/meta-fb-inbox/config.json" → resolve page URLbrowser action:"start" profile:"openclaw"browser action:"open" profile:"openclaw" targetUrl:<pageUrl> → get targetIdbrowser action:"act" profile:"openclaw" targetId:<targetId> request:{"kind":"wait","timeMs":4000}browser action:"snapshot" profile:"openclaw" targetId:<targetId> refs:"aria" compact:true<name> (<time>) <preview> [未讀/已讀]If the snapshot is hard to parse (Meta's DOM is complex), fall back to:
browser action:"screenshot" profile:"openclaw" targetId:<targetId>
Then describe what you see in the screenshot.
Why get URLs? Having the direct conversation URL lets you jump straight to a chat later without searching for it again. Saves time when replying.
When to get URLs: After listing chats, get URLs for unread conversations or any conversation you might need to access again.
📝 Simple 3-Step Method:
Click into the conversation using the customer name:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"evaluate","fn":"function() { const containers = document.querySelectorAll('div[style*=\"position: absolute\"]'); for (let container of containers) { const nameEl = container.querySelector('div.x1vvvo52.x1fvot60.xxio538'); if (nameEl && nameEl.textContent.trim() === '<customer_name>') { nameEl.click(); return {clicked: true}; } } return {error: 'not found'}; }"}
Replace <customer_name> with the actual customer name from your conversation list.
Wait 2 seconds for page to load and URL to update:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":2000}
Get the current URL - it's automatically updated!
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"evaluate","fn":"function(){return window.location.href;}"}
Store the URL alongside the chat info.
✅ Example URL Format:
https://business.facebook.com/latest/inbox/all/?&asset_id=123456789012345&selected_item_id=1234567890&thread_type=FB_MESSAGE
Or with optional mailbox_id:
https://business.facebook.com/latest/inbox/all/?&asset_id=123456789012345&mailbox_id=123456789012345&selected_item_id=9876543210&thread_type=FB_MESSAGE
🔍 Understanding the URL:
asset_id = Your Facebook page ID (stays the same)selected_item_id = Unique conversation ID (different for each customer)thread_type=FB_MESSAGE = Messenger conversation typemailbox_id = Optional, may appear for some conversations💾 How to Use Later:
Next time you need to access this conversation, skip all the searching:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<saved_conversation_url>"
Wait 2 seconds, and you're in the conversation!
💡 Tip: Get URLs for unread messages during your check routine, store them in a file or memory, and reuse them for instant access.
🔄 Getting Multiple URLs:
If you need URLs for multiple conversations:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<pageUrl>"
Wait 2 seconds.Alternative method (when already in conversation): If you're already viewing a conversation and just need its URL, skip step 1 and directly run step 3 - the URL is already there!
⚡ Fast Path: If you have the conversation URL from "Get URLs for Conversations":
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<conversation_url>"
Wait 2 seconds, then skip to step 3.
Standard Path:
To programmatically read messages:
Navigate to the conversation (if not already there).
Inject the read-messages script:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"evaluate","fn":"<contents_of_scripts/read-messages.js>"}
Read the script file:
read file_path:"skills/meta-fb-inbox/scripts/read-messages.js"
And pass its entire contents as the fn parameter (wrapped in parentheses).
Parse the response: The script returns an array of message objects:
[
{"text": "你好", "isCustomer": true, "hasImage": false, "imageUrl": null},
{"text": "[Image]", "isCustomer": true, "hasImage": true, "imageUrl": "https://scontent-..."}
]
Download images (if any):
⚠️ Default download location: ~/Downloads (do NOT clutter the workspace).
For each message with hasImage: true:
cd ~/Downloads
curl -O "<imageUrl>"
The downloaded file will use the original filename from the URL.
If you want a custom filename:
cd ~/Downloads
curl -o "fb-message-$(date +%Y%m%d-%H%M%S).jpg" "<imageUrl>"
⚡ Fast Path (if you have conversation URL):
If you already obtained the conversation URL from "Get URLs for Conversations" section, skip step 1 and go directly:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<conversation_url>"
Wait 2 seconds, then proceed to step 2 (Take snapshot to find input box).
Standard Path (when you don't have the URL):
Open the conversation:
Click the customer name in the conversation list:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"evaluate","fn":"function() { const containers = document.querySelectorAll('div[style*=\"position: absolute\"]'); for (let container of containers) { const nameEl = container.querySelector('div.x1vvvo52.x1fvot60.xxio538'); if (nameEl && nameEl.textContent.trim() === '<customer_name>') { nameEl.click(); return {clicked: true}; } } return {error: 'not found'}; }"}
Replace <customer_name> with the actual customer name.
Wait 2 seconds for the conversation to load.
Take a snapshot to find the input box:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for textbox with [active] attribute.
Type the reply message:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"type","ref":"<input_ref>","text":"<your_message>"}
After typing, wait 1 second for the send button to appear.
Take another snapshot to find the send button:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for a new button near the textbox. The send button appears after you type text (it replaces the "like" button).
Click the send button:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<send_button_ref>"}
Verify the message was sent:
Wait 2 seconds, then take a screenshot or check the conversation list to confirm the message appears.
⚡ Fast Path: If you have the conversation URL, navigate directly:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<conversation_url>"
Wait 2 seconds, then proceed to the label operations.
Open the conversation (if not already open, and you don't have URL).
Take a snapshot to locate the label's remove button:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for the Labels section in the right sidebar (appears before Notes section):
heading with level=3 (the Labels heading)button elementbutton with text "clearLabel"Click the clearLabel button:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<clearLabel_ref>"}
Example from snapshot:
- heading [level=3] [ref=eAAA] ← Labels heading
- button [ref=e949]: ← Label button (shows label name)
- button "clearLabel" [ref=e952] ← Remove button (THIS ONE!)
Click the clearLabel button (ref=e952) to remove the label.
Wait for the change to apply:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":1500}
Verify the label was removed (optional): Take a screenshot or snapshot to confirm the label is gone.
- combobox "label" [ref=eXXX]
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"type","ref":"<combobox_ref>","text":"<label_name>","submit":true}
⚡ Fast Path: If you have the conversation URL, navigate directly:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<conversation_url>"
Wait 2 seconds, then proceed to the note operations.
Question: Do you need to ADD or EDIT a note?
Take a snapshot of the conversation sidebar.
Look for the Notes section (usually appears after Labels section).
Check what you see:
ONLY see a "Add note" type button/link (single button, no existing note text) → Contact has NO note → Use "Add a New Note" section below.
See an "Edit" link + existing note text (usually with timestamp) → Contact has existing note → Use "Edit an Existing Note" section below.
Simple rule:
How to identify in snapshot:
button [ref=eXXX] or link [ref=eXXX] with NO paragraph text nearbylink [ref=eXXX] (edit) + link [ref=eYYY] (delete) + paragraph [ref=eZZZ]: "note text"The Notes section is located below the Labels section in the right sidebar. There are two states:
paragraph element)Best Practice: Keep only one note per contact. Use "Edit" to update existing notes rather than adding multiple notes.
In snapshot, look for these patterns:
No note:
- heading [level=3] [ref=eXXX] ← Notes heading
- button [ref=eYYY] ← Single "add note" button
Has note:
- heading [level=3] [ref=eXXX] ← Notes heading
- link [ref=eYYY] ← "add note" link
- text: "X minutes ago ·" ← Timestamp
- link [ref=eZZZ] ← Edit link (THIS ONE for editing!)
- text: "·"
- link [ref=eAAA] ← Delete link
- paragraph [ref=eBBB]: "note text" ← Existing note content
When to use: Contact already has a note. You will see an Edit link next to existing note text with timestamp.
Step-by-step:
Open the conversation (click customer name in chat list). Wait 2 seconds for it to load.
Take snapshot to find the Edit button:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for the pattern (Notes section):
- heading [level=3] [ref=eAAA] ← Notes heading
- link [ref=eBBB] ← "add note" link
- text: "4 hours ago ·" ← Timestamp
- link [ref=eXXX] ← Edit link (THIS ONE!)
- text: "·"
- link [ref=eYYY] ← Delete link
- paragraph [ref=eZZZ]: "Customer note" ← Current note
Key: Find the link that appears AFTER the timestamp text and BEFORE the "·" separator. That's your Edit button.
Click Edit button:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<edit_button_ref>"}
Wait for edit form:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":1500}
Take snapshot to find the textbox:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for:
- textbox [ref=eXXX]: "Customer note" ← Current text (contains existing note)
- button [ref=eYYY] ← Cancel button
- button [ref=eZZZ] ← Delete button
- button [disabled] [ref=eAAA] ← Save button (disabled until you change text)
Key: The textbox will have the current note text. The Save button will show [disabled] attribute.
Click textbox to focus:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<textbox_ref>"}
Select all existing text (Cmd+A):
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"press","key":"Meta+a"}
Type new text (replaces selected text):
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"type","ref":"<textbox_ref>","text":"Customer ID: 12345 ✅"}
Wait for Save button to enable:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":500}
Take snapshot to find enabled Save button:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for:
- button [ref=eXXX] [cursor=pointer]: ← Now enabled! (no [disabled] attribute)
Key: The Save button will now show [cursor=pointer] and will NOT have [disabled] attribute.
Click Save:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<save_button_ref>"}
Wait and verify:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":2000}
Take screenshot to confirm update.
Common mistakes to avoid:
When to use: Contact has NO existing note. You will see ONLY a single button/link (no existing note text).
Step-by-step:
Open the conversation (click customer name in chat list). Wait 2 seconds for it to load.
Take snapshot to find the "add note" button:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for the pattern (Notes section with NO existing note):
- heading [level=3] [ref=eAAA] ← Notes heading
- button [ref=eXXX] ← Single "add note" button (THIS ONE!)
Key: You should see ONLY one button/link under the Notes heading, with NO paragraph element containing note text.
Click "add note" button:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<button_ref>"}
Wait for the textbox:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":1500}
Take snapshot to find the textbox:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for:
- textbox [ref=eXXX]: ← Input field (may show "Hidden Label")
/placeholder: ... ← Placeholder text
- button [ref=eYYY] ← Cancel button
- button [disabled] [ref=eZZZ] ← Save button (disabled at first)
Key: The textbox might be labeled "Hidden Label" or similar. Look for [disabled] attribute on the Save button.
Type the note text:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"type","ref":"<textbox_ref>","text":"Customer note text here"}
Wait for Save button to become enabled:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":500}
Take snapshot to find the enabled Save button:
browser action:"snapshot" profile:"openclaw" targetId:"<targetId>" refs:"aria" compact:true
Look for:
- button [ref=eXXX] [cursor=pointer]: ← Now enabled (no [disabled] attribute!)
Click Save button:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"click","ref":"<save_button_ref>"}
Wait and verify:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"wait","timeMs":2000}
Take a screenshot to confirm the note appears in the sidebar.
Common mistakes to avoid:
In snapshot:
- link [ref=eXXX] ← Edit link
- text: "·"
- link [ref=eYYY] ← Delete link (THIS ONE!)
If multiple pages are configured:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<newPageUrl>"
https://business.facebook.com/latest/inbox/all/?&asset_id=<PAGE_ID>