feat(capi): server-side Meta Conversions API for signup (CompleteRegistration)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

The browser pixel under-counts signups badly: ~96% of paid traffic is mobile
in-app browsers where iOS ITP / ad-blockers drop client events, and the OAuth
path never fired it reliably. Meta recorded ~0 registrations for a 7.5K-spend
campaign while PostHog saw 98 facebook signups — so Meta could neither optimize
toward nor attribute signups, which is the main driver of the low signup rate.

This adds a server-side CAPI CompleteRegistration:
- MetaCapiService + @Global module. Fail-open: no-ops unless META_CAPI_PIXEL_ID
  + META_CAPI_ACCESS_TOKEN are set; never throws (signup must not break).
  SHA-256 hashed email + fbp/fbc/IP/UA.
- Fired from the better-auth user.create.after hook for ALL signups (reliable,
  covers Google OAuth which the browser pixel missed entirely).
- A session-gated POST /analytics/meta/complete-registration endpoint adds
  fbp/fbc/IP/UA (ad-click attribution) for the email path.
- The browser pixel now passes a shared event_id (signup_<userId>); the
  premature Google client-pixel fire (fired on click, before completion) is
  removed.
- All sources dedupe via event_id=signup_<userId>.

Activate by setting META_CAPI_PIXEL_ID + META_CAPI_ACCESS_TOKEN (Events Manager)
in the api env; META_CAPI_TEST_EVENT_CODE routes to Test Events for verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 15:19:58 +03:00
parent 3325747428
commit 145689a391
10 changed files with 223 additions and 5 deletions

View File

@@ -45,6 +45,11 @@ interface AuthOptions {
novu?: NovuService;
/** Called after a user's email is verified (referral qualification, etc.). */
onEmailVerified?: (userId: string) => Promise<void>;
/** Server-side Meta CAPI sender — reliable signup backstop for BOTH email and
* Google OAuth (the browser pixel misses OAuth and is ad-blocked on mobile). */
metaCapi?: {
sendCompleteRegistration: (input: { userId: string; email?: string | null }) => Promise<void>;
};
}
export function createAuth(
@@ -155,6 +160,14 @@ export function createAuth(
};
},
after: async (user) => {
// Server-side Meta CAPI signup event — reliable for BOTH email and
// Google OAuth (the browser pixel misses OAuth and is ad-blocked on
// mobile in-app). Deduped with the pixel/endpoint via
// event_id=signup_<id>. Fire-and-forget; never break signup.
void options?.metaCapi?.sendCompleteRegistration({
userId: user.id,
email: user.email,
});
// Fire-and-forget lifecycle e-mails on signup (covers password +
// OAuth). Never await / never let a notification failure surface
// into the signup response. `referral` is delay-stepped in Novu