setup-guides

Meta Pixel setup (Graphene FM)

What this enables: paid Meta ads (Facebook + Instagram) optimizing against graphene.fm traffic, and an audience cohort built from graphene.fm visitors only — not mixed with hivejournal.com's journaling audience.

The codebase already has the Meta Pixel component wired (apps/frontend/src/components/analytics/FacebookPixel.tsx). It's gated on two env vars; setup here is the operator workflow for getting the pixel ID + flipping the gates.


1. Get a pixel ID from Meta

Skip the platform-picker in the ad-creation dialog (Magento / Shopify / etc.) — graphene.fm is custom Next.js, none apply. Use Events Manager directly:

  1. https://business.facebook.com/events_manager/
  2. Left sidebar → Data sources
  3. If a pixel already exists from a prior surface (e.g. the HiveJournal-for-iOS Page from older iterations), you can reuse its 15-digit ID — the hostname gate in step 2 below keeps it Graphene-only at runtime.
  4. To create a fresh one: top-right + Connect data sourcesWeb → name it Graphene FM + URL https://graphene.fm → the 15-digit ID lands on the next screen.

When the wizard asks "How do you want to install?", pick "Manually install pixel code yourself" and IGNORE the snippet it shows — the install lives in FacebookPixel.tsx. You only need the ID.

The ID may also appear as "Dataset ID" in newer Meta UIs — same value, just renamed.


2. Set the Vercel env vars

In Vercel → Project → Settings → Environment Variables, add for Production AND Preview:

NEXT_PUBLIC_FB_PIXEL_ID=<the 15-digit ID from step 1>
NEXT_PUBLIC_FB_PIXEL_HOSTNAME=graphene.fm

The HOSTNAME var (added in PR #509) is what keeps the pixel from firing on hivejournal.com. The same Vercel deployment serves both hosts; without the hostname gate the pixel would mix journaling-app traffic into the audio-fiction-listener cohort and Meta's lookalike-audience suggestions get worse for both surfaces.

Redeploy. Verify by loading https://graphene.fm and confirming the Facebook Pixel Helper Chrome extension shows the pixel firing — then load https://www.hivejournal.com and confirm it does NOT fire.


3. Pixel-only is enough for the first traffic campaign

For a Traffic-objective ad (optimizing on link clicks), the browser pixel alone is sufficient. Meta will use it for:

  • PageView events (auto-fired on every route change)
  • Audience-build (graphene.fm visitors → lookalikes)
  • Conversion attribution (which clicks led to graphene.fm visits)

Existing event-tracking conventions in apps/frontend/src/lib/listen-tracking.ts already wire custom fbq('trackCustom', ...) calls for listen-progress events; those keep working with the new pixel once the env vars are set.


4. Conversions API (CAPI) — defer until conversion volume exists

The Meta Ads Manager will offer to also set up the Conversions API (a server-side complement to the browser pixel). It catches ~30-40% of conversions the browser pixel misses (ad-blocker users, iOS 14.5+ ATT opt-outs).

Defer this until week 2-3 of running ads, for two reasons:

  1. Pixel-only is sufficient for the Traffic objective. CAPI's value compounds when optimizing for conversion events (Purchase, Subscribe) which need a Sales-objective campaign, which needs ~50 conversion events/week before Meta's optimizer converges. At current trajectory (1 paying sub / 28d, per readout #489) the conversion volume to justify CAPI hasn't arrived yet.
  2. CAPI requires backend code to actually fire events. The Meta UI's CAPI setup wizard only declares intent — the events it lists (View Content, Subscribe, Purchase, etc.) need server code that POSTs to https://graph.facebook.com/<api_version>/<pixel_id>/events with the pixel access token + hashed user data. ~2-3 hours of work; do it once you actually need the iOS-recovered conversion data.

When you DO build CAPI:

  • Fire from the existing Stripe webhook (Subscribe + Purchase events) in apps/backend/src/routes/payment.ts
  • Fire Complete Registration from the email-confirm route in apps/backend/src/services/graphene-subscribers.ts
  • Fire View Content from a backend GET when an unauthenticated visitor lands on /seasons/[id], with the season metadata (uses the existing host-detection already in the page route for OG tag selection)
  • Mirror the recordHeartbeat pattern from docs/reference/RECURRING_TASKS.md so a CAPI delivery failure logs to service_errors rather than silently dropping events.

Don't build it before there's a conversion-objective ad campaign to optimize against.


Troubleshooting

  • Facebook Pixel Helper says "No pixels found" on graphene.fm → Check NEXT_PUBLIC_FB_PIXEL_ID is set in Vercel Production AND Preview, and that you redeployed since adding it. Env vars set after the last deploy don't take effect until the next one.
  • Pixel fires on hivejournal.com tooNEXT_PUBLIC_FB_PIXEL_HOSTNAME isn't set, OR is set to a value that doesn't match (case matters — gate is lowercase-suffix match). Set it to graphene.fm exactly.
  • Events Manager shows "0 events received" after a day → Walk through the site in an incognito window. The first PageView should appear within ~30s of loading any page.

  • Hostname-gated pixel rationale — PR #509 commit message has the full reasoning
  • FacebookPixel.tsx — the component
  • listen-tracking.ts — custom event wiring
  • Funnel readout #489 — baseline conversion numbers; informs the "defer CAPI" decision
META PIXEL SETUP — Docs | HiveJournal