| name | meta-implement |
| description | Drive the end-to-end implementation of the Meta (Facebook + Instagram) Graph API module into a React + TypeScript + Supabase project, sprint by sprint, enforcing the acceptance gate of each phase before moving on. Use when starting a Meta Ads or Instagram integration from scratch, resuming a partial one, or when asked to install, implement, port or set up Facebook OAuth, campaign sync, campaign creation, audiences, pixels or Instagram publishing. |
| license | MIT |
| compatibility | Target project needs React 18+, TypeScript, a Supabase project (Postgres, Edge Functions) and the Supabase CLI. A Meta app of type Business with the Marketing API and Instagram products is required to pass the sprint 1 gate; production scopes need App Review. |
| metadata | {"version":"1.0.0","part-of":"meta-graph-api"} |
Implement the Meta Graph API module
Executes a six-sprint plan. Each sprint ends in a gate — an observable
outcome, usually confirmed in Meta's own tooling (Ads Manager, Events Manager).
A failed gate stops the run.
Source material is in the sibling skill directory:
../meta-graph-api/references/ the plan (ORQUESTRADOR + sprint-0 … sprint-5)
../meta-graph-api/assets/ migrations, edge functions, hooks, components
In Claude Code that resolves under ${CLAUDE_SKILL_DIR}/../meta-graph-api/.
If the relative path fails:
find ~ -type d -path "*skills/meta-graph-api/assets" 2>/dev/null | head -3
Copy the packaged artifacts. Never retype an edge function or a migration —
they carry the 18 corrections listed in the orchestrator, and a rewrite silently
reintroduces the defects they fix.
Before sprint 0 — decide four things
Ask the user; record the answers in the project's CLAUDE.md / AGENTS.md.
- Tenancy. The artifacts use
company_id + get_user_company_ids(uuid).
What is the equivalent — org_id, workspace_id, or single-tenant? Decide
now: it renames a column across 17 migrations, every RLS policy and most of
the 30 functions.
- The client level. The origin links each
client to one ad account, one
Facebook page and one Instagram account. If your product has no such entity,
decide whether assets link straight to the tenant — and apply that
consistently, because the whole sync pipeline resolves assets through it.
- Scope. Sprints 0–2 are the minimum useful product (connect + sync +
read). Sprint 3 (creation), 4 (audiences/pixels) and 5 (Instagram) are
independent — confirm which are actually wanted.
- The Meta app. Type Business, with the Marketing API and Instagram
products added, the redirect URI registered, and — for production —
App Review for
ads_management, ads_read, business_management,
pages_* and instagram_*. Review takes days to weeks. If the app is not
ready, build sprints 0–1 against a Development-mode app with the developer as
a test user, and say so rather than faking the gate.
Also confirm the host-app adapter points in ../meta-graph-api/SKILL.md §4.
How to run each phase
- Read the phase document in
../meta-graph-api/references/ (one folder
per sprint, sprint-0 through sprint-5). It lists prerequisites, tasks,
artifacts and the gate. The ordering constraints are not obvious from the code.
- Check the prerequisites it names. Missing one → go back; do not improvise.
- Copy the artifacts, adapting only: the tenancy column/function, the
@/
import paths, and the adapter hooks. Nothing else.
- Apply and deploy:
supabase db push
supabase functions deploy <function-name>
Deploying and db push act on the user's real project — ask before the
first one of a session, then proceed.
- Run the gate and report the observed result, not the expected one.
- Report in one short block: created, adapted, gate result.
The sprints
Sprint 0 — Foundation
Phases: connection schema · campaign schema · RLS, GRANTs and config.
Applies the migrations from assets/migrations/ (read INDEX.md first — it
explains each file and the FK order), the tenancy helper, policies, GRANTs,
.env and config.toml.
Gate: migrations apply; select * from facebook_integrations returns an
empty result and not a permission error. An error here means the GRANT or the
helper is wrong — every later sprint depends on it.
Sprint 1 — OAuth connection
Phases: edge functions (_shared/meta-graph.ts, oauth, scope check, pages) ·
hooks · connection UI.
Three things the origin got wrong and this plan fixes — verify each:
Gate: connect a real account → ad accounts, pages and Instagram accounts
appear in the UI, and check-meta-scopes lists the granted scopes.
Sprint 2 — Sync and tree
Phases: sync engine · scheduler · tree hooks · tree UI.
The sync is an async job (sync_jobs), not a request that blocks. Insights
come nested in the campaign/adset/ad queries with the attribution window set.
Gate: for the same period, the numbers in the app match Ads Manager. If they
do not, it is almost always the attribution window
(action_attribution_windows(["7d_click","1d_view"])), not a sync bug.
Sprint 3 — Creation and editing
Phases: creation edge functions · builder wizard · unified editors.
Remember: Marketing API creation POSTs use
application/x-www-form-urlencoded, and an adset's ad account must match its
campaign's.
Gate: a campaign created from the app appears in Ads Manager with the right
objective, budget and targeting. Create it paused.
Sprint 4 — Audiences and pixels
Phases: audiences (7 actions) · pixels + Conversions API · interest search.
CAPI payloads must hash email and phone with SHA-256, lowercase and trimmed,
before sending. Sending raw PII to Meta is both a bug and a compliance problem.
Gate: a test event sent through CAPI appears in Events Manager; a custom
audience created from the app appears in Audiences.
Sprint 5 — Instagram
Phases: publishing + scheduler · AI auto-post · DM webhook, inbox and automation.
Two non-negotiables:
- publishing is container → publish, two calls;
- the DM webhook validates
x-hub-signature-256 (HMAC with the app secret) and
deduplicates by message id — without dedup, a Meta redelivery duplicates the
message and re-fires the automation.
Gate: a post publishes to Instagram; a DM sent to the account appears in the
inbox without a refresh and fires its keyword automation exactly once.
Rules for this run
- A failed gate stops the run. Report what failed and what the user must
decide. Do not carry a broken foundation forward.
- Never invent Graph API fields or endpoints. If something is missing from
the contract in
../meta-graph-api/SKILL.md §2 and the examples, say so and
check Meta's documentation.
- Never commit or log credentials. App Secret is an Edge Function secret;
user and page tokens live in the database and never reach the browser.
- One Graph API version, from
_shared/meta-graph.ts. A hardcoded version
anywhere else is the defect this plan exists to avoid.
- Create ad entities paused while testing. A live campaign spends real money.
When the run finishes
- Run the checks:
npx tsc --noEmit, lint, build, and
deno check supabase/functions/<name>/index.ts on every function touched.
- Write the module's context into the project's
CLAUDE.md / AGENTS.md:
## Meta module (Graph API)
Facebook + Instagram through the Graph API. One version constant in
`supabase/functions/_shared/meta-graph.ts`. User tokens in
`facebook_integrations`, page tokens in `facebook_pages` — never in the browser.
App Secret is an Edge Function secret only. The Instagram DM webhook is public
(`verify_jwt = false`) and validates HMAC.
Before changing anything here, load the `meta-graph-api` skill. Non-negotiable:
no inline Graph API version; no `TO anon` policies; every new table gets a GRANT;
Instagram publishing is container → publish; dedupe DMs by message id.
- Report: sprints completed, gates passed, what was skipped, and what the user
still needs from Meta (App Review, scopes, a linked Instagram account).