# @kody/meta — agent notes Human setup and intent live in [`README.md`](./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` | | instagram | `kody:@kody/meta/instagram` | | whatsapp | `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) ```ts 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 ```ts 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: ```ts 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. ```ts 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/oauth` URL; 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.