Conversion attribution
Conversion attribution
The anonymous visitor id
Sticky assignment and dedup need a stable per-visitor handle. That handle is a random
32-hex-char token in a first-party cookie (cbox_vid) — not a customer id, email, IP, or
fingerprint. It exists only to make a visitor's assignment stable and to dedupe
impressions/conversions; it carries no personal data, is never joined to a customer, and an
operator can drop it entirely without affecting billing.
Cookie policy: SameSite=Lax, HttpOnly (the server is the only reader), one-year lifetime. A
cross-site embed (an iframe on a third-party marketing site) will not receive a Lax
first-party cookie, so an embedded visitor is treated as a fresh anonymous visitor on each load —
a deliberate privacy trade-off. It lives in App\Billing\Experiments\VisitorIdentity.
Impressions
When a running experiment serves a variant's table, an impression is recorded for
(variant, visitor) — deduped to once per visitor per variant by a UNIQUE database index, so
a refresh or a return visit never inflates the denominator. Recording is best-effort: it never
breaks serving the page.
Conversions — the two moments
A conversion is attributed through the checkout deep-link. When a running experiment serves a variant, its attribution triple is threaded onto every CTA link as three query params:
| Param | Value |
|---|---|
cbox_exp |
the experiment key |
cbox_var |
the assigned variant id |
cbox_vid |
the anonymous visitor id |
The operator's checkout entry forwards these to POST /api/v1/checkout-sessions (as
experiment / variant / visitor). From there:
- Checkout started — the API mints the hosted checkout session and records a
checkout_startedconversion, stamped with the session id. The recorder validates the variant belongs to a running experiment whose key matches (deny-by-default — a stale link to a concluded or renamed experiment records nothing). - Checkout completed — when the gateway's settled webhook activates the subscription, the
settlement finds the started conversion(s) for that session and records the matching
checkout_completedconversion.
Idempotency
Every conversion row is UNIQUE on (variant, visitor, kind). So a double checkout-start (a
replayed CTA link) or a re-delivered settlement webhook hits the constraint and is swallowed —
a conversion is counted at most once. The completion path also inherits the webhook ingest's
own settle-once guard, so a double settlement never double-counts.
The recorder is App\Billing\Experiments\ConversionAttribution; the completion hook lives in the
existing CheckoutActivation decorator so it runs on the same exactly-once settled webhook that
creates the subscription.