Skip to content
← Community packages

Call Facebook Pages, Instagram, and WhatsApp Cloud through saved Meta OAuth or a system user token.

Browse files

  • Other
  • meta
  • facebook
  • pages
  • instagram
  • whatsapp
  • graph
  • oauth
  • system-user
  • multi-account
License
MIT
Published
August 22, 2026
Pinned commit
486272c
Rating
No ratings yet
Forks
0
Stars
0
Adaptation effort

README

@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)

  1. Open the Meta App Dashboard → 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 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

HostUsed for
graph.facebook.comGraph, Pages, Instagram, WhatsApp Cloud, token exchange
graph.instagram.comInstagram Graph host (some IG-only routes)
graph-video.facebook.comVideo upload
www.facebook.comOAuth authorize (included by the connect page)

Multiple accounts

Default OAuth name is meta. Extra accounts use meta-<purpose>:

Omitted integration / account resolve to meta / metaSystemUserToken. There is no reserved personal or brand-page alias.

Exports

ExportDescription
.Overview plus smokeTest()
./smoke-testRead-only GET /me smoke test (or setup URLs when disconnected)
./accountsIntegration naming (meta / meta-*, metaSystemUserToken)
./scopesPermission catalog and prefilled connect / secret URLs
./coregraphRequest, 403 scope errors
./pagesList/read Pages and posts; publishPost
./instagramDiscover IG accounts from Pages; media; publishMedia
./whatsappBusinesses, WABAs, phone numbers, templates; sendMessage
./requestGeneric Graph escape hatch
./typesShared 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

PermissionUsed by
public_profile./smoke-test
emailOptional profile field
pages_show_listList Pages, discover Instagram accounts
pages_read_engagement / pages_read_user_contentRead Page posts
pages_manage_postspublishPost
instagram_basicInstagram profile and media
instagram_content_publishpublishMedia
instagram_manage_commentsComments
instagram_manage_insightsIG insights
whatsapp_business_managementWABAs, phone numbers, templates
whatsapp_business_messagingsendMessage
business_managementList businesses

./request uses whatever permissions the saved token already has.

Docs

Report this listing

Log in to report this listing.