# @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. ## What it does - 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` - Resolve multi-account OAuth / system-user credentials via `./accounts` Do not use this package to scrape personal Facebook profiles, run a 24/7 bot, or paste access tokens into chat. ## Prerequisites / 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) 1. Open the [Meta App Dashboard](https://developers.facebook.com/apps) → Create app (type **Business**). 2. Add products you will call: **Facebook Login for Business**, **Instagram**, **WhatsApp**. 3. Facebook Login → Settings → Valid OAuth Redirect URIs: exactly `https://kody.codes/connect/oauth`. 4. App settings → Basic: copy App ID and App Secret. 5. 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. 6. 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`): https://kody.codes/connect/oauth?provider=meta&authorizeUrl=https%3A%2F%2Fwww.facebook.com%2Fv26.0%2Fdialog%2Foauth&tokenUrl=https%3A%2F%2Fgraph.facebook.com%2Fv26.0%2Foauth%2Faccess_token&flow=confidential&scopes=public_profile%2Cemail&scopeSeparator=%2C&allowedHosts=graph.facebook.com%2Cgraph.instagram.com%2Cgraph-video.facebook.com&apiBaseUrl=https%3A%2F%2Fgraph.facebook.com%2Fv26.0&dashboardUrl=https%3A%2F%2Fdevelopers.facebook.com%2Fapps&extraAuthorizeParams=%7B%22auth_type%22%3A%22rerequest%22%7D All products in this package: https://kody.codes/connect/oauth?provider=meta&authorizeUrl=https%3A%2F%2Fwww.facebook.com%2Fv26.0%2Fdialog%2Foauth&tokenUrl=https%3A%2F%2Fgraph.facebook.com%2Fv26.0%2Foauth%2Faccess_token&flow=confidential&scopes=public_profile%2Cemail%2Cpages_show_list%2Cpages_read_engagement%2Cpages_read_user_content%2Cpages_manage_posts%2Cpages_manage_engagement%2Cpages_manage_metadata%2Cpages_messaging%2Cread_insights%2Cbusiness_management%2Cinstagram_basic%2Cinstagram_content_publish%2Cinstagram_manage_comments%2Cinstagram_manage_insights%2Cinstagram_manage_messages%2Cwhatsapp_business_management%2Cwhatsapp_business_messaging&scopeSeparator=%2C&allowedHosts=graph.facebook.com%2Cgraph.instagram.com%2Cgraph-video.facebook.com&apiBaseUrl=https%3A%2F%2Fgraph.facebook.com%2Fv26.0&dashboardUrl=https%3A%2F%2Fdevelopers.facebook.com%2Fapps&extraAuthorizeParams=%7B%22auth_type%22%3A%22rerequest%22%7D 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](https://business.facebook.com/settings/system-users) is the unattended path. 1. Meta Business Settings → Users → System users → generate a token. 2. Assign the Facebook Page, Instagram professional account, and WhatsApp Business account assets the helpers should see. 3. Save the token on this form (never paste it into chat): https://kody.codes/account/secrets/new?name=metaSystemUserToken&description=Meta%20Business%20system%20user%20token%20for%20Facebook%20Pages%2C%20Instagram%2C%20and%20WhatsApp%20Cloud.%20Paste%20only%20on%20this%20form.&allowedHosts=graph.facebook.com%2Cgraph.instagram.com%2Cgraph-video.facebook.com&scope=user 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): https://kody.codes/account/secrets/new?name=metaAccessToken&description=Do%20not%20use%20for%20user%20OAuth.%20%40kody/meta%20prefers%20/connect/oauth%20or%20metaSystemUserToken.&allowedHosts=graph.facebook.com&scope=user ### 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-`: - Connect work: https://kody.codes/connect/oauth?provider=meta-work - Call with `integration: 'meta-work'` or `account: 'work'` - System user extras: `tokenSecret: 'metaSystemUserToken-work'` or `account: 'work'` with `authMode: 'system-user'` Omitted `integration` / `account` resolve to `meta` / `metaSystemUserToken`. There is no reserved personal or brand-page alias. ### 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. Agents: see [`AGENTS.md`](./AGENTS.md) for import paths, smoke checks, and mutation snippets. ## Done when - OAuth `meta` is connected or `metaSystemUserToken` is saved; hosts `graph.facebook.com`, `graph.instagram.com`, and `graph-video.facebook.com` are approved - Smoke test returns `{ ok: true, … }` (or `{ ok: false, setup }` with prefilled URLs when disconnected) and does not post or send - Mutations preview with `dryRun: true` and only run live with `confirm: true` after you approve the exact Page / IG / WhatsApp target ## Branding Official Meta infinity symbol from [Meta brand resources](https://www.meta.com/brand/resources/meta/company-brand/). 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](https://kody.codes/@kody/meta). ## Docs - [Facebook Login (manual flow)](https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow) — authorize / token URLs - [Permissions](https://developers.facebook.com/docs/permissions) - [WhatsApp Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started) - [OAuth](https://kody.codes/guides/oauth) — `/connect/oauth` query params - [Connect secret](https://kody.codes/guides/connect-secret) — `/account/secrets/new`