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 Fa...
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>