home / skills / openclaw / skills / facebook-page
This skill helps you manage a Facebook Page via the Graph API by loading credentials, composing requests, and handling errors securely.
npx playbooks add skill openclaw/skills --skill facebook-pageReview the files below or copy the command above to add this skill to your agents.
---
name: facebook-page
description: "Facebook Page manager: post, schedule, reply, get insights & more. Requires: powershell/pwsh. Reads ~/.config/fb-page/credentials.json (FB_PAGE_TOKEN, FB_PAGE_ID). FB_APP_SECRET for one-time setup only — delete afterward. Long-lived token; rotate periodically and immediately if host is compromised. Grant minimal permissions only. No data forwarded to third parties; all calls go to graph.facebook.com only."
metadata: {"openclaw":{"emoji":"[fb]","requires":{"anyBins":["powershell","pwsh"]}}}
---
# facebook-page — Universal Meta Graph API Skill
Constructs and executes Meta Graph API calls inline based on what the user wants. No scripts needed.
API version: **v25.0**
Base URL: `https://graph.facebook.com/v25.0`
---
## STEP 1 — Load Credentials
Credentials are stored in `~/.config/fb-page/credentials.json`.
```powershell
$cfg = Get-Content "$HOME/.config/fb-page/credentials.json" -Raw | ConvertFrom-Json
$token = $cfg.FB_PAGE_TOKEN
$pageId = $cfg.FB_PAGE_ID
```
**If the file doesn't exist**, guide setup. Required fields:
| Field | Purpose |
|---|---|
| `FB_PAGE_TOKEN` | Never-expiring Page access token — used for all API calls |
| `FB_PAGE_ID` | Numeric Facebook Page ID |
| `FB_APP_ID` | Meta App ID — only needed during token exchange |
| `FB_APP_SECRET` | Meta App Secret — only needed during token exchange |
**One-time token exchange setup:**
```powershell
# Provide: $appId, $appSecret, $shortToken (from Graph API Explorer), $pageId
# 1. Exchange for long-lived user token
$r1 = Invoke-RestMethod "https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id=$appId&client_secret=$appSecret&fb_exchange_token=$shortToken"
# 2. Get never-expiring Page token
$r2 = Invoke-RestMethod "https://graph.facebook.com/v25.0/$pageId?fields=access_token&access_token=$($r1.access_token)"
$pageToken = $r2.access_token
# 3. Save — only these four fields, nothing else
@{
FB_PAGE_ID = $pageId
FB_PAGE_TOKEN = $pageToken
FB_APP_ID = $appId
FB_APP_SECRET = $appSecret
} | ConvertTo-Json | Set-Content "$HOME/.config/fb-page/credentials.json" -Encoding UTF8
```
**Restrict file permissions immediately after saving:**
```powershell
# Windows
icacls "$HOME/.config/fb-page/credentials.json" /inheritance:r /grant:r "$($env:USERNAME):(R,W)"
# macOS / Linux
# chmod 600 ~/.config/fb-page/credentials.json
```
> âš ï¸ Never commit this file to version control. It contains long-lived secrets.
> This skill makes no external calls other than to `graph.facebook.com`. No data is forwarded to third parties.
---
## STEP 2 — Figure Out the API Call
### Common Endpoints
| What user wants | Method | Endpoint |
|---|---|---|
| Post text | POST | `/$pageId/feed` — body: `message` |
| Post with image | POST | `/$pageId/photos` — multipart: `source` + `message` |
| Post with video | POST | `/$pageId/videos` — multipart: `source` + `description` |
| Post with link | POST | `/$pageId/feed` — body: `message` + `link` |
| Delete a post | DELETE | `/{post-id}` |
| Schedule a post | POST | `/$pageId/feed` — body: `message` + `published=false` + `scheduled_publish_time` (unix timestamp) |
| Get recent posts | GET | `/$pageId/published_posts?fields=id,message,created_time&limit=10` |
| Get page info | GET | `/$pageId?fields=name,fan_count,followers_count,about` |
| Like a post | POST | `/{post-id}/likes` |
| Get comments | GET | `/{post-id}/comments?fields=message,from,created_time` |
| Reply to comment | POST | `/{comment-id}/comments` — body: `message` |
| Hide comment | POST | `/{comment-id}` — body: `is_hidden=true` |
| Delete comment | DELETE | `/{comment-id}` |
| Get page insights | GET | `/$pageId/insights?metric=page_fans,page_impressions&period=day` |
| Get post insights | GET | `/{post-id}/insights?metric=post_impressions,post_reactions_by_type_total` |
| List events | GET | `/$pageId/events?fields=name,start_time,description` |
| Create event | POST | `/$pageId/events` — body: `name`, `start_time`, `description` |
| List albums | GET | `/$pageId/albums?fields=name,count` |
| Get page roles | GET | `/$pageId/roles` |
| Publish draft post | POST | `/{post-id}` — body: `is_published=true` |
### API Call Patterns
**GET:**
```powershell
$result = Invoke-RestMethod -Uri "https://graph.facebook.com/v25.0/ENDPOINT?access_token=$token" -ErrorAction Stop
```
**POST (form body):**
```powershell
$result = Invoke-RestMethod -Uri "https://graph.facebook.com/v25.0/ENDPOINT" -Method POST `
-Body @{ field1="value1"; field2="value2"; access_token=$token } -ErrorAction Stop
```
**DELETE:**
```powershell
$result = Invoke-RestMethod -Uri "https://graph.facebook.com/v25.0/{id}?access_token=$token" -Method DELETE -ErrorAction Stop
```
**Multipart (image/video upload):**
```powershell
$boundary = [System.Guid]::NewGuid().ToString()
$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
$fileName = [System.IO.Path]::GetFileName($filePath)
$stream = New-Object System.IO.MemoryStream
$writer = New-Object System.IO.StreamWriter($stream)
$writer.Write("--$boundary`r`nContent-Disposition: form-data; name=`"message`"`r`n`r`n$message`r`n")
$writer.Write("--$boundary`r`nContent-Disposition: form-data; name=`"access_token`"`r`n`r`n$token`r`n")
$writer.Write("--$boundary`r`nContent-Disposition: form-data; name=`"source`"; filename=`"$fileName`"`r`nContent-Type: image/jpeg`r`n`r`n")
$writer.Flush(); $stream.Write($fileBytes, 0, $fileBytes.Length)
$writer.Write("`r`n--$boundary--`r`n"); $writer.Flush()
$result = Invoke-RestMethod -Uri "https://graph.facebook.com/v25.0/$pageId/photos" -Method POST `
-ContentType "multipart/form-data; boundary=$boundary" -Body $stream.ToArray() -ErrorAction Stop
```
**Scheduled post** — convert local time to Unix timestamp:
```powershell
$runAt = [datetime]"2026-03-15 09:00"
$unixTime = [int][double]::Parse(($runAt.ToUniversalTime() - [datetime]"1970-01-01").TotalSeconds)
$result = Invoke-RestMethod -Uri "https://graph.facebook.com/v25.0/$pageId/feed" -Method POST `
-Body @{ message="text"; published="false"; scheduled_publish_time=$unixTime; access_token=$token } -ErrorAction Stop
```
---
## STEP 3 — Handle Errors
```powershell
try {
# ... API call ...
} catch {
$err = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue
$code = $err.error.code
$subcode = $err.error.error_subcode
$msg = $err.error.message
}
```
| Code | Subcode | Meaning | Fix |
|---|---|---|---|
| 100 | — | Invalid parameter | Check the parameter values |
| 102 | — | Session expired | Re-run setup to get a new token |
| 190 | 460 | Token expired | Re-run setup with a new short-lived token |
| 190 | 467 | Invalid token | Re-run setup |
| 200 | — | Permission denied | Add the permission listed in `error.message` to your app |
| 10 | — | Permission denied (page) | Add `pages_read_engagement` or `pages_manage_posts` |
| 230 | — | Requires re-auth | Re-run setup |
| 368 | — | Temporarily blocked | Wait and retry; page may be rate-limited |
### Permissions Reference
| Permission | Required for |
|---|---|
| `pages_manage_posts` | Create, delete, schedule posts |
| `pages_read_engagement` | Read posts, likes, comments, insights |
| `pages_show_list` | List pages you manage |
| `pages_manage_metadata` | Update page settings |
| `pages_manage_engagement` | Moderate comments, reply to reviews |
| `pages_read_user_content` | Read visitor posts and comments |
| `pages_manage_ads` | Manage ad campaigns on the page |
| `pages_manage_instant_articles` | Manage Instant Articles |
**If a permission is missing:**
1. Go to [Meta for Developers](https://developers.facebook.com/apps/)
2. Select your app → Permissions and Features
3. Add the required permission
4. Regenerate token via [Graph API Explorer](https://developers.facebook.com/tools/explorer/)
5. Re-run setup with the new token
---
## AGENT RULES
- **Always load credentials first.** If missing or incomplete, guide setup.
- **Only use `FB_PAGE_TOKEN` and `FB_PAGE_ID`** for API calls. `FB_APP_ID` and `FB_APP_SECRET` are for token exchange only.
- **Never write extra fields** to the credentials file (no owner IDs, conv IDs, or third-party keys).
- **Remove FB_APP_SECRET** from credentials.json after token exchange — it is not needed for API calls.
- **Least-privilege:** only request the permissions your use case needs. Do not request `pages_manage_ads` or `pages_manage_instant_articles` unless explicitly needed.
- **Rotate FB_PAGE_TOKEN** periodically via Graph API Explorer, and immediately if the host is ever compromised.
- **All API calls go to `graph.facebook.com` only.** No external forwarding, no third-party services.
- **Construct API calls inline** from user intent — don't look for script files.
- **On any error:** parse `error.code` + `error.error_subcode`, map to the table above, tell the user exactly what to do.
- **If a permission is missing:** name it, link to Meta for Developers, say to re-run setup.
This skill is a Facebook Page manager that performs Graph API calls to post, schedule, reply, fetch insights and manage content for a Page. It reads credentials from ~/.config/fb-page/credentials.json and uses a long-lived page access token to talk only to graph.facebook.com. The skill enforces least-privilege, token rotation guidance, and keeps secrets local—no third-party forwarding.
On start the skill loads FB_PAGE_TOKEN and FB_PAGE_ID from ~/.config/fb-page/credentials.json. It constructs appropriate Meta Graph API requests (v25.0) for actions like POST /{pageId}/feed, POST /{pageId}/photos, GET /{pageId}/insights, DELETE /{object-id}, and executes them against graph.facebook.com. For one-time token exchange the skill can guide you to use FB_APP_ID and FB_APP_SECRET, then instructs removal of the secret afterward. Errors are parsed for error.code and error.error_subcode and mapped to clear remediation steps.
What must be in credentials.json?
Must include FB_PAGE_TOKEN and FB_PAGE_ID. FB_APP_ID and FB_APP_SECRET are only required during one-time token exchange; delete FB_APP_SECRET afterward.
How do I fix permission errors?
Identify the missing permission from the error.message, add it in Meta for Developers under your app’s Permissions and Features, regenerate a token via the Graph API Explorer, and re-run setup.
Can this skill send data to third parties?
No. All API calls go only to graph.facebook.com; the skill does not forward any data to third parties.
How do I handle token expiry?
If token-related error codes appear (190, subcodes 460/467), perform the one-time exchange to obtain a new long-lived page token and update credentials.json, then rotate periodically.