@bountain/attribution-kit
v1.0.3
Published
Drop-in helpers to capture and verify Bountain click attribution (click_id + click_token) in Next.js.
Readme
@bountain/attribution-kit
Drop-in helpers to capture and verify Bountain click attribution (click_id + signed click_token) in Next.js App Router.
What it solves
- Visitors arrive at your site with:
click_id(UUID)click_token(HMAC-signed short-lived eligibility token)
- You want to persist that token as a first-party HttpOnly cookie and later verify it on the server when recording a conversion.
Install
This repo is a pnpm workspace. From the root:
pnpm add @bountain/attribution-kit
(When published, this will work the same from any repo.)
1) Add a persist route (server)
Create src/app/api/attribution/persist/route.ts:
import { createPersistAttributionRoute } from '@bountain/attribution-kit/server';
export const { GET, POST } = createPersistAttributionRoute();This route sets cookies:
yd_click_id(non-HttpOnly)yd_click_token(HttpOnly)
2) Capture on your landing page (client)
In any page.tsx that receives traffic, add a tiny client component:
'use client';
import { useAttributionCapture } from '@bountain/attribution-kit/client';
export function AttributionCapture() {
useAttributionCapture({ endpoint: '/api/attribution/persist' });
return null;
}Then render it in your page:
import { AttributionCapture } from './AttributionCapture';
export default function Page() {
return (
<main>
<AttributionCapture />
{/* ... */}
</main>
);
}3) Verify on conversion (server)
Example in a route handler or server action:
import { verifyAttributionFromRequest } from '@bountain/attribution-kit/server';
export async function POST(req: Request) {
const secret = process.env.APP_SECRET;
if (!secret) return new Response('missing APP_SECRET', { status: 500 });
const verified = verifyAttributionFromRequest(req, { secret });
if (!verified.ok) {
// analytics-only conversion
return new Response(JSON.stringify({ ok: true, eligible: false, error: verified.error }), {
status: 200,
headers: { 'content-type': 'application/json' },
});
}
// eligible conversion; verified.clickId binds lineage to a real click
return new Response(JSON.stringify({ ok: true, eligible: true, clickId: verified.clickId }), {
status: 200,
headers: { 'content-type': 'application/json' },
});
}Notes
- A browser cannot set an HttpOnly cookie, so capture must call a server endpoint.
- Token verification uses Node
crypto(HMAC SHA-256). - If verification fails (
expired,bad_signature, etc.), record the conversion as analytics-only.
