@kody/meta
Call Facebook Pages, Instagram, and WhatsApp Cloud through saved Meta OAuth or a system user token.
AGENTS.md
118 lines · 3.8 KB · Markdown@kody/meta — agent notes
Human setup and intent live in README.md. This file is for
agents: imports, smoke/dryRun execute snippets, and edge cases. Secrets and
OAuth by name only — never paste tokens. Do not disable live webhooks or
jobs. Never hard-code Page, Instagram, or phone-number ids.
Auth
| Call | Resolves to |
|---|---|
| omit | OAuth meta |
account: 'work' | meta-work / metaSystemUserToken-work |
integration: 'meta-work' | that exact OAuth name |
authMode: 'system-user' | secret metaSystemUserToken (or tokenSecret) |
tokenSecret: 'metaSystemUserToken-work' | that exact system-user secret |
Hosts: graph.facebook.com, graph.instagram.com, graph-video.facebook.com.
API base: https://graph.facebook.com/v26.0.
Import paths
| Export | Import |
|---|---|
overview + smokeTest | kody:@kody/meta |
| accounts | kody:@kody/meta/accounts |
| smoke-test | kody:@kody/meta/smoke-test |
| scopes | kody:@kody/meta/scopes |
core (graphRequest) | kody:@kody/meta/core |
| pages | kody:@kody/meta/pages |
kody:@kody/meta/instagram | |
kody:@kody/meta/whatsapp | |
| request | kody:@kody/meta/request |
| types | kody:@kody/meta/types |
Prefer static kody:@kody/meta/... imports from execute. Do not lead with
packages.invoke. Named helpers also re-export from product modules
(listPages, publishPost, listInstagramAccounts, sendMessage, …).
Smoke test (read-only)
import smokeTest from 'kody:@kody/meta/smoke-test'
export default async function main() {
return await smokeTest()
// or: smokeTest({ integration: 'meta-work' })
// or: smokeTest({ authMode: 'system-user' })
// ok => { ok: true, authMode, integration, id, name } (no post/send)
// missing creds => { ok: false, setup: { …URLs } }
}Common snippets
import { listPages } from 'kody:@kody/meta/pages'
import { listInstagramAccounts } from 'kody:@kody/meta/instagram'
import { listBusinesses } from 'kody:@kody/meta/whatsapp'
export default async function main() {
const pages = await listPages({ limit: 5 })
const ig = await listInstagramAccounts()
const businesses = await listBusinesses()
return { pages, ig, businesses }
}Escape hatch:
import request from 'kody:@kody/meta/request'
export default async function main() {
return await request({ path: '/me', query: { fields: 'id,name' } })
}dryRun / confirm
publishPost, publishMedia, sendMessage, and mutating ./request /
graphRequest require confirm: true. Pass dryRun: true to preview without
writing.
import { listPages, publishPost } from 'kody:@kody/meta/pages'
import { listPhoneNumbers, sendMessage } from 'kody:@kody/meta/whatsapp'
export default async function main() {
const pages = await listPages({ limit: 5 })
const preview = await publishPost({
pageId: pages.items[0]?.id,
message: 'Preview only',
dryRun: true,
})
const phones = await listPhoneNumbers({})
const waPreview = await sendMessage({
phoneNumberId: phones.items?.[0]?.id,
to: '15551234567',
body: 'Preview only',
dryRun: true,
})
return { preview, waPreview }
}Resolve WhatsApp phoneNumberId from listPhoneNumbers — never a hard-coded
brand number. After the user approves the exact target + body, retry with
confirm: true (not only dryRun: false).
Edge cases
- 403 / missing permission errors name the scope and the next
/connect/oauthURL; do not hide Page / IG / WhatsApp helpers when a scope is missing. - User OAuth tokens expire; prefer
authMode: 'system-user'for unattended jobs. - Do not scrape personal Facebook profiles or paste tokens into chat.
- Package-owned jobs are not enabled on this package — do not invent schedules that disable or rewrite live webhooks.