Features
Revenue attribution
Tie every payment back to the visitor, session and campaign that earned it. Connect your payment provider in about two minutes — no code — or use the SDK for full order-level analytics. Both paths are protected against double counting.
Overview
Payments are confirmed on your payment provider's servers — not in the browser. A webhook from Stripe, Lemon Squeezy or Polar is the authoritative record of revenue, but it arrives with no cookies, so on its own FlowGrid can't know which visitor paid. Attribution works by round-tripping two ids through the provider:
- The FlowGrid tracker drops
visitor_idandsession_idcookies as visitors browse. - When you create a checkout, you copy those ids into the provider's metadata.
- The payment webhook carries the metadata back — FlowGrid reads it and joins the revenue to the visitor's full journey: session, landing page, referrer, UTM campaign.
2-minute setup (no code)
FlowGrid hosts the webhook for you — nothing to deploy:
- In the dashboard, open Website settings → Payments and pick your provider.
- Copy the webhook URL shown, e.g.
https://core.flow-grid.xyz/api/v1/attribution/webhook/stripe?web_id=… - Paste it into your provider's dashboard (Stripe: Developers → Webhooks; Lemon Squeezy / Polar: Settings → Webhooks), select the payment events listed in the flow, and save the signing secret back in FlowGrid.
Done — payments, refunds and subscription revenue start flowing into the Monetization dashboards. Signatures are verified per site with your signing secret.
Attribute revenue to a visitor
Without metadata, revenue still records — it just can't be tied to a visitor or campaign. Add two keys when you create the checkout and attribution lights up. If you use the npm SDK server entry:
1import { cookies } from "next/headers";
2import { checkoutAttributionFromCookies } from "flowgrid-sdk/server";
3
4// Stripe
5const session = await stripe.checkout.sessions.create({
6 line_items, mode: "payment", success_url, cancel_url,
7 metadata: { ...checkoutAttributionFromCookies(await cookies()) },
8 // → { flowgrid_visitor_id, flowgrid_session_id }
9});
10
11// Lemon Squeezy: checkoutData.custom — Polar: metadata (same keys)No SDK? Read the cookies yourself and set the same keys — or, on Stripe only, put the visitor id in client_reference_id:
1// The FlowGrid tracker sets these cookies on your site:
2// visitor_id (365 days) session_id / fg_session_id (~30 min)
3metadata: {
4 flowgrid_visitor_id: readCookie("visitor_id"),
5 flowgrid_session_id: readCookie("session_id") ?? readCookie("fg_session_id"),
6}Client-side checkouts
Creating the checkout in the browser? fg.checkoutAttribution() in the npm SDK returns the same two keys — send them to your checkout endpoint with the price id.
SDK path — full order-level analytics
If you want line items, order lifecycle (shipped / delivered), refund reasons and subscription MRR events under your own control, handle the provider webhook yourself and report with flowgrid-sdk/server:
1import { attributionFromMetadata } from "flowgrid-sdk/server";
2import { fg } from "@/lib/flowgrid.server";
3
4const { visitorId, sessionId } = attributionFromMetadata(event.data.object);
5
6await fg.trackPurchase(
7 { order, userId: session.client_reference_id },
8 { visitorId, sessionId },
9);The full server API — purchases, refunds, order lifecycle and subscriptions — is documented in Server-Side Tracking. Both paths can run at the same time — see below.
Client-side auto-detect
The no-code tracker (flowgrid.js) also spots checkout returns on its own: when a visitor lands back on your site with a provider id in the URL (Stripe session_id=cs_…, Lemon Squeezy order_id, Polar checkout_id), it marks the conversion against the visitor immediately — before any webhook arrives. This is a marker, not a revenue record: the amount comes from the webhook, so auto-detect never inflates revenue.
Double-counting protection
The same order can reach FlowGrid more than once — a browser thank-you page and a server webhook, or a provider retrying a webhook delivery. Revenue events with a natural identity are deduplicated at ingestion:
- Purchases by
orderId, refunds byrefundId. - Order shipped / delivered once per
orderId; subscription start and trial conversion once persubscriptionId. - Connected-provider webhooks once per provider transaction id, however many times the provider retries.
Duplicates are acknowledged (not errored), so nothing retries. Recurring events — renewals, plan changes, order status updates, cart activity — are never deduplicated. Use the same orderId everywhere you report an order (e.g. the Stripe checkout-session id) and you can safely track from the browser and the server at once.