Skip to content
adscapi

Pixel helpers

Client-side click-id capture → first-party cookie → server-side parseClickIds() → conversions.track({ clickIds }). No third-party pixels required.

pixelSnippet(opts?)

ts
import { pixelSnippet, parseClickIds, CLICK_ID_PARAMS } from 'adscapi';

// Returns a minimal JS string to embed in a <script> tag
const js: string = pixelSnippet({
  endpoint?: string,  // optional POST target for { clickIds, url }
  cookie?: string,    // first-party cookie name (default: '_adscapi')
});

On load the snippet:

  1. Reads CLICK_ID_PARAMS from location.search
  2. Reads Meta’s _fbc / _fbp cookies
  3. Merges with the existing first-party cookie (default name _adscapi, ~90 days, SameSite=Lax)
  4. Writes the cookie back
  5. If endpoint is set, POSTs { clickIds, url } as JSON with keepalive

Inputs are JSON-escaped. No external calls beyond the optional endpoint POST. Dependency-free — safe to drop into any page.

parseClickIds(input)

ts
// Server-side: normalize a captured bag into ConversionClickIds
const clickIds = parseClickIds(
  input: Record<string, string | undefined> | string | null | undefined,
): ConversionClickIds;
// Accepts a parsed object OR a JSON string (e.g. cookie value). Never throws.

Keeps known keys + any key matching /clid$|_click|callback|braid/i. Drops empty values. Never throws — bad JSON becomes {}.

CLICK_ID_PARAMS

ts
const CLICK_ID_PARAMS: Record<string, string> = {
  gclid: 'gclid',
  fbclid: 'fbclid',
  msclkid: 'msclkid',
  ttclid: 'ttclid',
  twclid: 'twclid',
  ob_click_id: 'ob_click_id',
  li_fat_id: 'li_fat_id',
  gbraid: 'gbraid',
  wbraid: 'wbraid',
  epik: 'epik',   // Pinterest
  sccid: 'sccid', // Snapchat
};

Example

ts
// --- client: app/layout.tsx (or any page <head>) ---
import { pixelSnippet } from 'adscapi';

export default function RootLayout({ children }) {
  const snippet = pixelSnippet({
    endpoint: '/api/click-ids', // optional beacon
    cookie: '_adscapi',
  });
  return (
    <html>
      <head>
        <script dangerouslySetInnerHTML={{ __html: snippet }} />
      </head>
      <body>{children}</body>
    </html>
  );
}

// --- server: read the cookie and track ---
import { createAdscapi, parseClickIds } from 'adscapi';
import { cookies } from 'next/headers';

const ads = createAdscapi();

export async function POST(req: Request) {
  const body = await req.json();
  const jar = await cookies();

  const clickIds = parseClickIds(jar.get('_adscapi')?.value);
  // or merge with anything the client POSTed:
  // const clickIds = parseClickIds({ ...JSON.parse(jar.get('_adscapi')?.value ?? '{}'), ...body.clickIds });

  await ads.conversions.track({
    name: 'purchase',
    value: body.amount,
    currency: 'USD',
    user: { email: body.email },
    clickIds,
    consent: { adUserData: true, adPersonalization: true },
  });

  return Response.json({ ok: true });
}

Related