A complete Meta module — Facebook Ads and Instagram — packaged as agent skills for Claude Code and OpenCode.
Install it, and your coding agent can build the whole integration into a React + TypeScript + Supabase project: Facebook OAuth for ad accounts, pages and Instagram accounts; campaign/adset/ad sync with insights; a management tree with inline editing; end-to-end campaign creation through the Marketing API; custom and lookalike audiences; pixels with the Conversions API; and the Instagram block — publishing, AI auto-post, DM inbox and keyword automation.
What ships: 17 SQL migrations · 30 Deno edge functions · 17 React Query hooks · 71 components · 8 pages · 21 planning documents · Graph API payload samples.
Extracted from a multi-tenant ad-management platform running this in production — and it does not replicate that system's defects.
The plan maps the origin system's real defects and the artifacts fix them. The five that matter most:
| Defect in the original | Consequence |
|---|---|
| OAuth never does the long-lived token exchange | Stores a 60-day expires_at on a 1–2h token: the integration dies on its own while the database says it is valid |
| DM webhook with no HMAC validation | Anyone with the URL injects fake messages into the inbox |
No GRANT on any table of the module |
The frontend gets an empty array with no error — silent failure |
| Public dashboard read every client's data | An anon policy with no token filter, using the anon key that ships in the bundle |
DM webhook does not dedupe by mid |
A Meta redelivery duplicated the message and re-fired the automation |
The rest — five Graph API versions living side by side, ad sets and ads with no foreign keys, nullable tenant columns, action endpoints left unauthenticated, Realtime never enabled for the DM inbox — are in the orchestrator.
/plugin marketplace add bittencourtthulio/meta-graph-api-integration
/plugin install meta-graph-api-integration@bittencourtthulio-meta
git clone https://github.com/bittencourtthulio/meta-graph-api-integration.git
cd meta-graph-api-integration
./install.sh # this project → .claude/skills/
./install.sh --global # every project → ~/.claude/skills/
./install.sh --opencode # this project → .opencode/skills/
./install.sh --opencode --global # every project → ~/.config/opencode/skills/OpenCode reads .claude/skills/ natively, so either layout works there.
| Skill | Invoke | What it does |
|---|---|---|
meta-graph-api |
automatic, or /meta-graph-api |
The Graph API contract, the three token types, conventions, adapter points, failure modes |
meta-implement |
/meta-implement |
Builds it into your project, sprint by sprint, enforcing each gate |
meta-debug |
/meta-debug |
Isolates which link broke — token, scope, wrong account, or format |
Typical first run:
/meta-implement
> Add Meta Ads to this project. We use org_id for tenancy, no client entity —
> assets link straight to the tenant. Sprints 0 to 2 only for now.
| Stack | React 18+ · TypeScript · Supabase (Postgres + RLS, Deno Edge Functions) |
| Meta app | Type Business, with the Marketing API and Instagram products, redirect URI registered |
| App Review | Production scopes (ads_management, ads_read, business_management, pages_*, instagram_*) require review — days to weeks |
| A Professional account linked to a Facebook Page | |
| UI | Components assume shadcn/ui + Tailwind; hooks and edge functions port unchanged |
skills/meta-graph-api/
├── SKILL.md the contract, conventions, failure modes (English)
├── references/ the six-sprint plan, 21 documents (Brazilian Portuguese)
│ ├── ORQUESTRADOR.md master plan + the full 18-correction table
│ └── sprint-0…5/ one document per phase: tasks, artifacts, gate
└── assets/
├── migrations/ 17 SQL files, numbered in apply order
├── edge-functions/ 30 Deno functions + _shared/meta-graph.ts
├── frontend/hooks/ 17 React Query hooks
├── frontend/components/ 71 components (connection, tree, builder,
│ audiences, pixels, Instagram, DM inbox)
├── frontend/pages/ 8 pages
└── examples/ Graph payloads, insights contract, OAuth scopes,
IG publishing, webhook payloads, .env.example
skills/meta-implement/SKILL.md
skills/meta-debug/SKILL.md
Reference docs are in Brazilian Portuguese; the skill bodies are in English.
Each ends in a gate confirmed in Meta's own tooling — not in your logs.
| Sprint | Delivers | Gate |
|---|---|---|
| 0 Foundation | Connection + campaign schema, RLS, GRANTs, config | SELECT returns empty, not a permission error |
| 1 OAuth | Shared Graph module, OAuth with long-lived exchange, scopes, pages | Connect an account → ad accounts, pages and IG accounts appear |
| 2 Sync & tree | Async sync job with insights, scheduler, campaign tree | Numbers match Ads Manager for the same period and attribution window |
| 3 Create & edit | Full campaign creation, adset/ad CRUD, builder wizard | A campaign created from the app appears in Ads Manager |
| 4 Audiences & pixels | Audiences, pixels, Conversions API, interest search | A CAPI event shows in Events Manager |
| 5 Instagram | Publishing, AI auto-post, DM webhook + inbox, automation | A post publishes; a DM arrives and fires its automation once |
Sprints 0–2 are the minimum useful product. 3, 4 and 5 are independent.
- A token that "worked for weeks and stopped." The OAuth exchange returns a short-lived token; without the second exchange you store a 60-day expiry on a two-hour token.
- Numbers that disagree with Ads Manager. Almost never a sync bug — it is the
attribution window (
7d_click,1d_view). - Ad set creation failing with an error that names nothing. Usually the ad set's ad account differs from its campaign's.
- Instagram posts that never appear. Publishing is two calls: create the container, then publish it.
- DMs arriving twice, with the automation replying twice. Meta redelivers; dedupe by message id.
insightscoming back empty instead of erroring when a scope is missing.- "URL blocked" from one trailing slash in the redirect URI.
- The App Secret is backend-only. A leak lets someone impersonate your app against every user who authorized it.
- User tokens can spend ad budget. Never log them, never send them to the browser.
- The DM webhook is public. HMAC validation over the raw body is not optional.
- Meta's Platform Terms apply. Ad data belongs to the advertiser — do not aggregate it across tenants. Automated messaging must respect Instagram's 24-hour window; bulk unsolicited DMs get apps banned.
- CAPI payloads carry personal data — hash email and phone with SHA-256, lowercase and trimmed, before they reach Meta.
This repository contains no credentials: no tokens, no App ID or Secret, no real ad account, page or pixel ids, no project refs. Every example value is a placeholder.
Useful PRs: adapters for other tenancy models, Graph API version bumps, a Google Ads sibling module, and translations of the reference docs.
Known gaps, documented rather than hidden:
- Reference documentation is Brazilian Portuguese only.
- Components are coupled to shadcn/ui and Tailwind.
- Pinned to Graph API v21.0; Meta deprecates versions roughly yearly.
- The edge functions were syntax-checked when packaged, not exercised in CI.
MIT — see LICENSE.