SDK reference
@trace/sdk reads the Trace cookies (trace_visitor_id, trace_session_id) and shapes them as a metadata object you can pass straight to Stripe. One line, framework-aware, server- or client-side.
Install
npm install @trace/sdk
# or
pnpm add @trace/sdk
# or
yarn add @trace/sdkSubpath imports
The SDK ships one entry point per environment. Pick the one that matches your stack — they all return the same { trace_visitor_id, trace_session_id } shape.
| Import | Use when |
|---|---|
@trace/sdk/next | Next.js App Router (route handlers, server actions, server components) |
@trace/sdk/next-pages | Next.js Pages Router (pages/api/*) |
@trace/sdk/express | Express, Fastify, Hono, or anything that gives you a request object with req.headers.cookie |
@trace/sdk/browser | Client-side checkout where you call Stripe.js directly from the browser |
@trace/sdk/node | Generic Node — pass any Cookie header string in |
Next.js App Router
// app/api/checkout/route.ts
import { getAttribution } from '@trace/sdk/next';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { line_items } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items,
success_url: 'https://yourstore.com/thanks',
cancel_url: 'https://yourstore.com/cart',
metadata: await getAttribution(),
});
return Response.json({ url: session.url });
}Works in any server context that has access to cookies() from next/headers — route handlers, server actions, server components.
Next.js Pages Router
// pages/api/checkout.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { getAttribution } from '@trace/sdk/next-pages';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: req.body.line_items,
success_url: 'https://yourstore.com/thanks',
cancel_url: 'https://yourstore.com/cart',
metadata: getAttribution(req),
});
res.json({ url: session.url });
}Express / Fastify / Hono
// server.ts
import express from 'express';
import { getAttribution } from '@trace/sdk/express';
import Stripe from 'stripe';
const app = express();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
app.post('/api/checkout', express.json(), async (req, res) => {
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: req.body.line_items,
success_url: 'https://yourstore.com/thanks',
cancel_url: 'https://yourstore.com/cart',
metadata: getAttribution(req),
});
res.json({ url: session.url });
});The Express helper reads req.headers.cookie directly, so it works with any framework that follows that convention.
Browser (client-side checkout)
If you build your Checkout Session on the server but kick off Stripe.js from the browser, send the attribution from the client and have your server pass it through:
// client
import { getAttribution } from '@trace/sdk/browser';
const res = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
line_items: cart.items,
attribution: getAttribution(),
}),
});// server — pass body.attribution straight into metadata
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: body.line_items,
metadata: body.attribution,
success_url: '...',
cancel_url: '...',
});Generic Node (any Cookie header)
import { getAttributionFromCookieHeader } from '@trace/sdk';
const meta = getAttributionFromCookieHeader(req.headers.cookie ?? '');
// → { trace_visitor_id?: string; trace_session_id?: string }Use this if you're on a Node-flavored framework we don't ship a helper for (Hapi, Koa-with-no-cookie-parser, Cloudflare Workers, etc.).
The shape it returns
type Attribution = {
trace_visitor_id?: string;
trace_session_id?: string;
};Both keys are optional. If a user lands on your checkout from a fresh browser with no Trace cookies (e.g. they came from an email or a paste of the cart URL), the SDK returns {}. That's safe — Stripe accepts empty metadata.
Subscriptions, payment links, and elements
- Subscriptions — pass attribution to
stripe.subscriptions.createthe same way:{ metadata: await getAttribution() }. - Payment Links— these don't support per-visitor metadata. Trace falls back to email matching. Fine for low-volume PLG flows; not recommended as your primary checkout.
- Stripe Elements / Payment Element — pass attribution into the PaymentIntent
metadatawhen you create it on the server.