@classytic/arc-analytics
v0.2.1
Published
Arc module for server-side marketing conversions — a first-party /collect ingest that relays storefront events (Meta Conversions API + GA4 Measurement Protocol) with a shared eventId for browser↔server dedup. Wraps @classytic/analytics; host injects the p
Readme
@classytic/arc-analytics
Arc module for server-side marketing conversions. A stateless, first-party
/collect ingest that relays storefront events to Meta Conversions API and
GA4 Measurement Protocol, deduped against the browser pixel via a shared
eventId. Wraps the pure @classytic/analytics builders; the host injects a
config bridge and permission gate.
Why a module (not a browser pixel)
Ad-blockers and iOS ITP drop ~30% of browser purchase events. This endpoint is
first-party — blockers target facebook.com / google-analytics.com, not
your origin — so it fires when the browser pixel can't. Because the storefront
keys the event id to the order id, Meta/GA4 collapse the browser pixel and
this server twin into one conversion; if the pixel was blocked, the server fires
alone (the recovery).
Compose
import { createAnalyticsModule } from '@classytic/arc-analytics';
import { allowPublic } from '@classytic/arc/permissions';
createApp({
modules: [
createAnalyticsModule({
// Host bridge: public pixel ids from your settings store + SECRET tokens
// from env. Return { enabled: false } to no-op (master switch / unconfigured).
resolveConfig: async () => {
const cfg = await loadPlatformConfig();
if (cfg.marketing?.enabled === false) return { enabled: false };
return {
enabled: true,
meta: cfg.marketing?.metaPixelId && process.env.META_CAPI_ACCESS_TOKEN
? { pixelId: cfg.marketing.metaPixelId, accessToken: process.env.META_CAPI_ACCESS_TOKEN }
: undefined,
ga4: cfg.marketing?.ga4MeasurementId && process.env.GA4_API_SECRET
? { measurementId: cfg.marketing.ga4MeasurementId, apiSecret: process.env.GA4_API_SECRET }
: undefined,
};
},
permissions: { collect: allowPublic() }, // guests convert too
}),
],
});Mounts POST /marketing/collect (override via prefix). The storefront posts
{ event, user?, fbp?, fbc?, gaClientId?, sourceUrl? }; the module adds request
IP/UA, hashes PII (SHA-256, server-only), and forwards.
Boundaries
- No config knowledge — everything host-specific enters through
resolveConfig. - No order-kernel coupling — it never reads the order; the storefront hands it the conversion. (A future variant can subscribe to order events for tab-closed coverage.)
- Secrets stay in env — public pixel ids may live in a settings doc, but CAPI tokens / GA4 api_secret must never reach the browser; the bridge reads them from env.
