FullVision
Tracker Setup

Revenue Attribution

Wire two metadata fields into your Stripe object creates so FullVision links every paying customer to the visitor who acquired them

Pass two fields — fv_visitor_id and fv_user_id — on every Stripe Checkout Session, PaymentIntent, and Subscription you create. FullVision picks them up from the webhook and links the resulting customer to the visitor (and your authenticated user) automatically.

Use it when you have the tracker installed and payments are showing up with no channel attached to them.

This is the strongest customer-to-visitor signal we can read. It runs as a pre-pass before the email-based fallback, so when both signals are wired your attribution survives email typos, billing-vs-login splits, and address changes.

Prerequisites

  • The FullVision tracker must be installed on your site (so visitors get a fv_v cookie). See First-Party Tracker Proxy if you've enabled proxy mode.
  • Your app must have a stable external_id for each authenticated user — the same value you send as user_id to POST /v1/events.

The two fields

FieldSourceRequired?
fv_visitor_idRaw value of the FullVision visitor cookie (fv_v), read server-side at create timeSend when present
fv_user_idYour app's authenticated user ID (the external_id you already pass to /v1/events)Send when authenticated

Send only what you have at create time. Either field alone is enough; both together is best.

The three Stripe creates

Set metadata on every Stripe Checkout Session, PaymentIntent, and Subscription you create.

const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  customer: stripeCustomerId,
  line_items: [...],
  metadata: {
    fv_visitor_id: visitorCookie,
    fv_user_id: appUserId,
  },
});

If your checkout flow chains creates (e.g. a Checkout Session that creates a Subscription), set metadata on each independently. FullVision picks the latest non-null signal across all three sources, so duplication is safe and gives you a fallback if one webhook is delayed.

Do not put these fields on Charges, Refunds, or Invoices. Those objects are not in FullVision's metadata read path.

Sourcing the IDs

fv_visitor_id

Read the FullVision visitor cookie server-side at the moment you make the Stripe call. Pass the raw cookie value verbatim — no decoding, hashing, or transformation.

import { cookies } from "next/headers";

const visitorCookie = cookies().get("fv_v")?.value ?? null;

If the visitor has no cookie yet (first request, blocked, brand-new session), pass null or omit the field. The visitor-only and user-only paths both work.

fv_user_id

Pass the authenticated user's stable ID — the same value you already send to /v1/events as user_id. This must match byte-for-byte.

const appUserId = session?.user?.id ?? null;

If the user is not authenticated yet (anonymous checkout), pass null or omit the field.

What happens on each combination

fv_user_idfv_visitor_idWhat happens
presentpresentCustomer linked immediately on the next webhook
presentabsentCustomer linked immediately (user ID wins)
absentpresent, visitor knownCustomer linked immediately via the visitor → user mapping
absentpresent, visitor anonymousCustomer deferred — auto-resolves the moment the visitor authenticates and /v1/events arrives with both IDs
absentabsentFalls back to email matching (the legacy path)

The deferred case is what makes anonymous checkout work end-to-end. A visitor pays before signing up, the row sits unlinked, then the user authenticates and your existing /v1/events call provides the missing identity — FullVision picks up the deferred row automatically. No extra wiring.

Verification

After deploying, confirm the wiring is landing.

Are the metadata fields landing?

Look at a customer you know just checked out, through the documented People endpoint:

curl -H "Authorization: Bearer $FV_API_KEY" \
  "https://data.fullvision.io/people?q=jane@acme.com"

A customer whose metadata landed comes back with a real acquisition channel in first_touch_channel rather than Non-attributed. If a brand-new payer is still Non-attributed a few minutes after checkout, the metadata is not arriving.

Still seeing nothing?

If new payers keep arriving unattributed, the metadata is not reaching Stripe. Check that metadata is genuinely being passed to your stripe.*.create calls — log it once at the call site — and that the Stripe webhook destination points at your FullVision workspace.

Things to avoid

Edge cases

How it relates to the email-based fallback

FullVision still links customers by email when metadata is absent — that path is unchanged and works for any customer who hits the system without metadata. The metadata path runs first; email is the fallback.

When you wire metadata, new customers get the stronger signal automatically. Existing customers keep working off email. There's nothing to migrate — just deploy the metadata change and the gradient improves over time.

On this page