Meta Facebook 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 results are large. Do NOT repeat them in thinking.
- Extract needed info and process immediately.
- Preferred approach: Use
snapshot refs:"aria" compact:trueto find element refs, thenact+click/typewith ref.
Configuration
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 Format
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"
}
]
}
Resolving a Page
When the user asks to check messages:
- If they specify an alias (e.g. "fb fanpage 2"), look it up in
config.json→pages. - If they don't specify, use the first page in the array.
- If only one page exists, use it directly.
- If multiple pages exist and no alias given, list available aliases and ask which one.
Enter Facebook Inbox
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
targetIdandurlfrom 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;}"}- If URL contains
business.facebook.comand shows the inbox → logged in, proceed. - If redirected to a login page → notify the user they need to log in manually in the OpenClaw browser, then retry.
- If URL contains
-
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>"
Quick Check (for cron / automated checks)
Complete flow: open page → screenshot → report.
- Read config:
read file_path:"skills/meta-fb-inbox/config.json"→ resolve page URL - Start browser if needed:
browser action:"start" profile:"openclaw" - Open page:
browser action:"open" profile:"openclaw" targetUrl:<pageUrl>→ gettargetId - Wait:
browser action:"act" profile:"openclaw" targetId:<targetId> request:{"kind":"wait","timeMs":4000} - Check URL to verify login (see step 5 above)
- Take snapshot:
browser action:"snapshot" profile:"openclaw" targetId:<targetId> refs:"aria" compact:true - Look for conversation items in the inbox list. Each conversation typically shows:
- Customer name
- Last message preview
- Timestamp
- Unread indicator (bold text or dot)
- Report results in format:
<name> (<time>) <preview> [未讀/已讀] - Clean up tabs.
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.
Check Inbox Messages
- Follow "Enter Facebook Inbox" steps to get to the inbox page.
- Take a snapshot or screenshot to see the conversation list.
- Report conversations with name, time, preview, and read/unread status.
Get URLs for Conversations (取得對話網址 - Simple!)
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:
- Get URL for first conversation (steps 1-3 above)
- Navigate back to inbox list:
Wait 2 seconds.browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<pageUrl>" - Repeat for next conversation.
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!
View Specific Conversation
⚡ 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:
- From the inbox list, find and click the conversation (use snapshot + click by ref).
- Wait for messages to load (2-3 seconds).
- Take a screenshot to see the message thread.
- Report the messages with sender, time, and content.
Extract Messages with Images
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
fnparameter (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>"
Reply to a Message
⚡ 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:trueLook for
textboxwith[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:trueLook for a new
buttonnear 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.
Manage Labels
⚡ 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.
Remove a Label
-
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:trueLook for the Labels section in the right sidebar (appears before Notes section):
- A
headingwith level=3 (the Labels heading) - Each label appears as a
buttonelement - Inside each label button, there's a nested
buttonwith text "clearLabel" - The clearLabel button is the "×" (remove) icon
- A
-
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.
Add a Label
- Open the conversation.
- Take a snapshot to find the label input combobox:
- combobox "label" [ref=eXXX] - Type the label name:
browser action:"act" profile:"openclaw" targetId:"<targetId>" request:{"kind":"type","ref":"<combobox_ref>","text":"<label_name>","submit":true} - Wait and verify.
Manage Notes (備註)
⚡ 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.
Quick Decision Guide (看這裡!)
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:
- No note = Add new (only one button visible)
- Has note = Edit existing (edit link + note text visible)
How to identify in snapshot:
- No note: Look for pattern like
button [ref=eXXX]orlink [ref=eXXX]with NO paragraph text nearby - Has note: Look for pattern like
link [ref=eXXX](edit) +link [ref=eYYY](delete) +paragraph [ref=eZZZ]: "note text"
Note Structure
The Notes section is located below the Labels section in the right sidebar. There are two states:
- No note exists: You will see a single button or link (typically the "add note" action).
- Note exists: You will see:
- The note text (in a
paragraphelement) - An Edit link (usually appears before the note text with timestamp)
- A Delete link (next to edit link)
- Sometimes an "add note" link (for additional notes, but we prefer single-note approach)
- The note text (in a
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
Edit an Existing Note (編輯註記 - Simple Steps for AI)
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:trueLook 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 noteKey: 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:trueLook 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:trueLook 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:
- Don't forget to select all (Cmd+A) before typing — otherwise you append to existing text!
- Don't skip the wait after clicking — buttons need time to load.
- Always take a fresh snapshot to get new ref numbers.
Add a New Note (新增註記 - Simple Steps for AI)
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:trueLook 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:trueLook 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:trueLook 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:
- Don't skip snapshots — you need the ref numbers!
- Don't reuse old ref numbers — they change every snapshot.
- Wait after clicking before taking next snapshot.
Delete a Note
- Take a snapshot to find the Delete link (appears next to Edit link when a note exists).
- Click the Delete link (the link that appears AFTER the "·" separator, next to the Edit link).
- Confirm the deletion if prompted.
In snapshot:
- link [ref=eXXX] ← Edit link
- text: "·"
- link [ref=eYYY] ← Delete link (THIS ONE!)
Switch Between Pages
If multiple pages are configured:
- Read config to see all available pages.
- Navigate to the desired page URL:
browser action:"navigate" profile:"openclaw" targetId:"<targetId>" targetUrl:"<newPageUrl>" - Wait for the page to load.
Notes
- Meta Business Suite sessions expire periodically; re-login may be required.
- Facebook's DOM structure is complex and changes frequently. Prefer screenshots over DOM parsing when snapshots are unreliable.
- The inbox URL format:
https://business.facebook.com/latest/inbox/all/?&asset_id=<PAGE_ID> - Some pages may require specific permissions in Meta Business Suite.