@kody/meta
README.md
192 lines · 9.8 KB · Markdown@kody/meta
Official Meta infinity symbol from Meta brand resources. Paths are unmodified. Meta, Facebook, Instagram, WhatsApp, and the Meta logo are trademarks of Meta Platforms, Inc. This package is not affiliated with or endorsed by Meta.
Share https://kody.codes/@kody/meta.
Intent
One Meta helpers package for Kody agents: Facebook Pages, Instagram professional accounts, and WhatsApp Cloud API. Auth is a saved Meta OAuth connection (meta / meta-*) or a Business system user token secret — not an API key and not a bot token. Identity and asset ids always come from the connected token. This package never hard-codes a Page, Instagram, or phone-number id.
Do not split WhatsApp Cloud into a separate @kody/whatsapp while this package covers it.
When To Use
- List Facebook Pages the token can manage, read posts, and preview a Page publish
- Discover Instagram professional accounts linked to those Pages, list media, and preview a publish
- Discover WhatsApp Business accounts and Cloud phone numbers, list templates, and preview a send
- Call uncommon Graph routes with
./request
Do not use this package to scrape personal Facebook profiles, run a 24/7 bot, or paste access tokens into chat.
Required setup
There is no built-in Kody Meta app. Tokens are stored by Kody. Never paste an app secret, user token, or system user token into chat.
Redirect URI (OAuth): https://kody.codes/connect/oauth
Lane A — Facebook Login OAuth (BYO Meta app)
- Open the Meta App Dashboard → Create app (type Business).
- Add products you will call: Facebook Login for Business, Instagram, WhatsApp.
- Facebook Login → Settings → Valid OAuth Redirect URIs: exactly
https://kody.codes/connect/oauth. - App settings → Basic: copy App ID and App Secret.
- Permissions / use cases: add the scopes below. App Review is required for assets you do not admin; Development mode works for app admins and testers.
- Open a prefilled connect URL while signed in to Kody. Paste the App ID and App Secret only on that form.
Smoke test (public_profile + email):
All products in this package:
Reconnect an existing connection:
https://kody.codes/connect/oauth?provider=meta
Decoded connect settings:
- Authorize:
https://www.facebook.com/v26.0/dialog/oauth - Token:
https://graph.facebook.com/v26.0/oauth/access_token - Flow:
confidential(App Secret on the token request) - Extra authorize params:
{"auth_type":"rerequest"}so declined permissions can be requested again - API base:
https://graph.facebook.com/v26.0
A 403 / permission error from this package names the missing scope and the next /connect/oauth URL. Page, Instagram, and WhatsApp helpers are not hidden when a token is missing a scope.
Facebook user tokens expire (short-lived, then ~60 days after exchange). Prefer a system user token for unattended jobs.
Lane B — Business system user token
For Pages, Instagram assets, and WhatsApp Cloud that a Business already owns, a system user token is the unattended path.
- Meta Business Settings → Users → System users → generate a token.
- Assign the Facebook Page, Instagram professional account, and WhatsApp Business account assets the helpers should see.
- Save the token on this form (never paste it into chat):
Call helpers with authMode: 'system-user' (and tokenSecret only to override the name). Extra accounts use metaSystemUserToken-work.
Wrong path (do not use for Graph access tokens you already have in chat):
Required hosts
| Host | Used for |
|---|---|
graph.facebook.com | Graph, Pages, Instagram, WhatsApp Cloud, token exchange |
graph.instagram.com | Instagram Graph host (some IG-only routes) |
graph-video.facebook.com | Video upload |
www.facebook.com | OAuth authorize (included by the connect page) |
Multiple accounts
Default OAuth name is meta. Extra accounts use meta-<purpose>:
- Connect work: https://kody.codes/connect/oauth?provider=meta-work
- Call with
integration: 'meta-work'oraccount: 'work' - System user extras:
tokenSecret: 'metaSystemUserToken-work'oraccount: 'work'withauthMode: 'system-user'
Omitted integration / account resolve to meta / metaSystemUserToken. There is no reserved personal or brand-page alias.
Exports
| Export | Description |
|---|---|
. | Overview plus smokeTest() |
./smoke-test | Read-only GET /me smoke test (or setup URLs when disconnected) |
./accounts | Integration naming (meta / meta-*, metaSystemUserToken) |
./scopes | Permission catalog and prefilled connect / secret URLs |
./core | graphRequest, 403 scope errors |
./pages | List/read Pages and posts; publishPost |
./instagram | Discover IG accounts from Pages; media; publishMedia |
./whatsapp | Businesses, WABAs, phone numbers, templates; sendMessage |
./request | Generic Graph escape hatch |
./types | Shared TypeScript types |
Mutations accept dryRun: true and skip the write. Live writes also require confirm: true after explicit user approval of the exact change. Package-owned jobs are not enabled.
Smoke test
After connect or reconnect, import the published package (not packages.invoke):
import { smokeTest } from 'kody:@kody/meta'
export default async function main() {
return await smokeTest()
// or: smokeTest({ integration: 'meta-work' })
// or: smokeTest({ authMode: 'system-user' })
}A successful result looks like { ok: true, authMode, integration, id, name } and does not post or send. Missing credentials return { ok: false, setup } with the prefilled /connect/oauth and /account/secrets/new URLs.
Page discovery after OAuth:
import { listPages } from 'kody:@kody/meta/pages'
export default async function main() {
return await listPages({ limit: 5 })
}Example
import { listPages, publishPost } from 'kody:@kody/meta/pages'
import { listInstagramAccounts } from 'kody:@kody/meta/instagram'
import { listBusinesses, sendMessage } from 'kody:@kody/meta/whatsapp'
export default async function main() {
const pages = await listPages({ limit: 5 })
const ig = await listInstagramAccounts()
const businesses = await listBusinesses()
const preview = await publishPost({
pageId: pages.items[0]?.id,
message: 'Preview only',
dryRun: true,
})
const waPreview = await sendMessage({
phoneNumberId: 'YOUR_PHONE_NUMBER_ID',
to: '15551234567',
body: 'Preview only',
dryRun: true,
})
return { pages, ig, businesses, preview, waPreview }
}Replace YOUR_PHONE_NUMBER_ID with an id from listPhoneNumbers — never a hard-coded brand number.
Scopes
| Permission | Used by |
|---|---|
public_profile | ./smoke-test |
email | Optional profile field |
pages_show_list | List Pages, discover Instagram accounts |
pages_read_engagement / pages_read_user_content | Read Page posts |
pages_manage_posts | publishPost |
instagram_basic | Instagram profile and media |
instagram_content_publish | publishMedia |
instagram_manage_comments | Comments |
instagram_manage_insights | IG insights |
whatsapp_business_management | WABAs, phone numbers, templates |
whatsapp_business_messaging | sendMessage |
business_management | List businesses |
./request uses whatever permissions the saved token already has.
Docs
- Facebook Login (manual flow) — authorize / token URLs
- Permissions
- WhatsApp Cloud API
- OAuth —
/connect/oauthquery params - Connect secret —
/account/secrets/new