What this lets you do
Connect Stripe to Traceten and see which AI sources drive paying customers, not just pageviews. When a Stripe charge succeeds, Traceten matches it back to the originating session and attributes the revenue to the correct AI source.Before you start
- A Traceten account with at least one site tracked.
- A Stripe account with live mode access, and permission to create restricted API keys on it.
- The Traceten snippet installed and verified on the site where Stripe Checkout runs.
- Workspace admin in Traceten. Connecting and disconnecting a revenue source affects the whole workspace, so only admins can do it.
Connect Stripe
You connect Stripe by pasting a restricted API key. There is no OAuth redirect, and there is no webhook to configure: Traceten creates the webhook endpoint on your Stripe account for you.1
Create a restricted key in Stripe
Open Stripe → Developers → API keys and click Create restricted key.Give it a name (for example,
Traceten) and set these permissions:Leave everything else set to None. Webhook Endpoints needs Write because Traceten creates the endpoint for you. Everything else is read-only.Payment Intents and Balance Transactions are not used on every payment, only when a charge comes in in a currency Traceten cannot price directly (for example, an ECB-unlisted currency). In that rare case Traceten reads the charge’s Balance Transaction, which carries the amount already converted into your Stripe account’s own settlement currency, so the payment is still recorded instead of being dropped. Without these two permissions that fallback simply does not run for you and such a charge is skipped, not attributed incorrectly.Click Create key, then copy it. It starts with
rk_live_.2
Paste it into Traceten
Go to app.traceten.com/dashboard, open Settings → Integrations, and choose the site your checkout runs from at the top of the page.Pick Stripe from the list of payment providers, then paste the restricted key.Click Connect.
3
Confirm the connection
Stripe shows as Connected, with your account name, your Stripe default currency, and the last four characters of the key.Traceten stores the key encrypted. After this point it is never shown again, in the dashboard or in the API, and only those last four characters are readable back.
What Traceten does with the key
In the same request, Traceten:- Reads your account name, account ID and default currency from Stripe.
- Reads one charge and one Checkout Session, to prove the key can actually see the revenue it is being connected for. Both are read, because the import uses both.
- Creates a webhook endpoint on your Stripe account pointed at Traceten, subscribed to exactly seven events.
- Stores the key and the webhook signing secret, both encrypted.
- Starts importing your last 90 days of completed Checkout Sessions and paid invoices.
Subscription revenue arrives on
invoice.payment_succeeded. Stripe bills a renewal as an invoice, so every renewal is attributed to the AI source that won the customer, and it carries the subscription id into the per-subscription lifetime value report. The two customer.subscription.* events are on the endpoint, but no revenue, conversion or lifetime-value figure is computed from them.
charge.succeeded is on the endpoint and is deliberately not turned into revenue. A charge behind a Checkout Session or an invoice is already counted by one of the two events above, and counting the charge as well would double that order.
Refunds and net revenue
Your attributed revenue is net of refunds. When Stripe refunds a charge, Traceten subtracts that amount from the AI source the original payment was attributed to.- The refund is dated on the day it was issued, not the day of the original charge. A past day’s revenue figure never changes after the fact, and a day on which you refunded more than you took in reads as a negative number.
- Conversion counts stay gross. A refunded charge still counts as a conversion. The money is reported separately as a refunded figure beside the net one.
- Partial refunds subtract exactly what Stripe returned. A Stripe refund states its own amount rather than a running total, so two partial refunds on one charge subtract each amount once.
- A refund for a charge Traceten never attributed leaves your totals alone. A charge from before you connected Stripe, or one outside the 90-day import, added no revenue in the first place, so there is nothing to take back off. Traceten holds such a refund and retries it rather than dropping it, in case the charge arrives later.
- A cancelled subscription is not a refund. Cancelling stops future charges. The charges already paid were earned and stay in your totals, so cancelling changes no figure in Traceten. Only a refund takes money back off.
If the key is missing a permission
Traceten checks the key against Stripe before storing anything, and the error names the exact permission Stripe says is missing. Four permissions are proved at connect: Account read, Charges read, Checkout Sessions read, and Webhook Endpoints write. A key that can create the endpoint but cannot read charges or Checkout Sessions is refused too, rather than connecting and then failing the import. The most common cause is leaving Webhook Endpoints on Read or None. Traceten will not fall back to a slower method: without that permission there is no webhook, and without a webhook there is no revenue attribution. Payment Intents and Balance Transactions read are not checked at connect time. They are only exercised the first time a charge arrives in a currency Traceten cannot price directly. If they are missing, that one charge is skipped rather than attributed with a wrong or missing amount; add both permissions and replace the key if you see this. Nothing is saved and no endpoint is created, so you can add the permission in Stripe and paste the key again. That holds whether this is a first connect or a replacement: when you are replacing a key, Traceten creates the new endpoint and stores it before removing the old one, so a refused replacement leaves your existing connection exactly as it was.Keys Traceten will not accept
- Secret keys (the ones starting
sk_) are rejected. A secret key can do anything in your Stripe account, including moving money. Traceten does not need that and will not hold it. - Publishable keys (starting
pk_) are rejected. They cannot read the data Traceten needs. - Test-mode restricted keys (
rk_test_) are rejected in production. Test-mode charges carry synthetic amounts that would corrupt your revenue totals.
Connecting Stripe with multiple sites
Stripe connects to one site, and each site connects its own Stripe account. If you track multiple sites under one account, connect Stripe once per site, choosing the site first.1
Open the site you want to attribute
In the dashboard, open Settings → Integrations and choose the site whose revenue you want to attribute.
2
Connect that site's Stripe account
Paste a restricted key from the Stripe account that bills that site’s customers. Traceten pins the
connection to the site you started from.
3
Repeat for each site
Do the same for every other site, each with the Stripe account that charges its customers.
- A Stripe account can be connected to only one site at a time. Reconnecting a Stripe account that is already linked to another site moves the connection to the new site and removes revenue attribution from the first one, with no error, so long as the new site has no Stripe connection of its own. If it already has one, Traceten refuses rather than replacing it; disconnect that one first. If several sites all charge through one Stripe account, only one site can hold that connection at a time; split billing into separate Stripe accounts if each site needs its own revenue attribution.
- Payments are matched only within the connecting site. A charge from a site’s Stripe account is attributed to sessions on that same site. Revenue never crosses between sites.
How attribution works
When a payment settles, Stripe sends acheckout.session.completed and/or
invoice.payment_succeeded webhook to Traceten. Traceten then tries to tie the
payment back to the originating session, in priority order:
client_reference_id: if you passed the visitor’s_traceten_vidcookie through asclient_reference_id, Traceten matches it to the originating session exactly. This field only exists oncheckout.session.completed.- Payment Link redirect: if you set the Payment Link’s confirmation page to
your own site with
?session_id={CHECKOUT_SESSION_ID}, the Traceten snippet on that page reports the Checkout Session id and Traceten matches it to the browser that completed the purchase. See below. - Email hash: if the buyer’s session called
traceten.identify({ email }), Traceten matches the payment’s customer email (hashed, never stored) to that visitor. This is the only path available to invoice-only and subscription-renewal payments, which never carry aclient_reference_id. - No match: the payment is still recorded so revenue totals stay complete, but it shows as unattributed (no AI source).
The consumer also accepts
metadata.traceten_session_id, checked before client_reference_id if
present. It is intentionally not documented as something to send: it pins to whichever session was
live when the Checkout Session was created, which skips the visitor-id match’s own resolution,
the one that already finds the visitor’s most recent AI-referred session even when a more
recent non-AI session exists. For a returning visitor, passing session_id can attribute to the
wrong (or no) source in exactly the case this product exists to catch. client_reference_id alone
is both sufficient and the better match.Payment Links: attribute without writing code
A bare Stripe Payment Link never goes through your server, so there is nowhere to setclient_reference_id. Instead, have Stripe hand the Checkout Session id back to your own site after payment, and let the snippet do the rest.
1
Open the Payment Link in Stripe
Stripe dashboard, Product catalogue → Payment Links → your link → Edit.
2
Set the confirmation page to your own site
Under After payment, choose Don’t show confirmation page and pick Redirect to your website (Stripe labels this “Confirmation page”). Enter a page on your site that has the Traceten snippet installed, with the session parameter appended:
{CHECKOUT_SESSION_ID} is Stripe’s own template variable. Stripe replaces it with the real session id when it redirects the buyer. Type it exactly as shown, braces included.3
Save and buy something through the link
Complete a real purchase. The buyer lands on your thank-you page, the snippet reads
session_id out of the URL, and the payment appears against its AI source in Revenue, usually within a minute or two.- You change nothing in your code. The only edit is the redirect URL in Stripe.
- Stripe must be connected to Traceten. Traceten ignores a thank-you-page report for a site whose Stripe account is not connected, and does not hold it until you connect. A report can also be discarded for up to a minute after you connect.
- The order of events does not matter. Stripe’s webhook usually reaches Traceten before the buyer’s browser finishes redirecting. When that happens the payment is recorded first and re-attributed the moment the browser reports in, on the original payment’s own date.
- The thank-you page must load within 30 minutes of payment. Traceten pairs a report with a payment only when the two are no more than 30 minutes apart. Stripe’s redirect happens straight after payment, so this only matters for a thank-you page opened again much later.
- The thank-you page needs the snippet. If the redirect points somewhere the snippet is not installed, nothing is reported and the payment stays unattributed.
- Payment Links with “Create invoices” turned on work too. Stripe then sends both the checkout and an invoice for one payment. Traceten records the revenue once, against whichever event reaches it first, and matches the thank-you page report to that record either way. If both events are processed at the same moment, the report can miss the pairing and the payment keeps whatever match it already had.
- One-time payments are covered in both directions; subscriptions only when the browser gets there first. A subscription’s revenue is recorded against the invoice rather than the checkout, and the invoice carries no Checkout Session id, so there is nothing to match a late redirect against. For subscription Payment Links, pass
client_reference_idfrom your own checkout flow where you can.
client_reference_id, where you can use it, is exact.
Guest checkout: pass the visitor ID via client_reference_id
Stripe Checkout, Payment Links, and Pricing Tables capture the buyer’s email for the first time inside Stripe’s hosted page. By then the visitor session has already ended, so there is no email on your domain to match against. The fix is to pass the visitor’s pseudonymous Traceten identifier through to Stripe as client_reference_id when you create the Checkout Session. Stripe surfaces the value on the checkout.session.completed event, and Traceten matches it back to the originating AI session exactly, with no email required.
The robust way to get the value is window.traceten.getVisitorId(), called client-side and forwarded to whatever creates the Checkout Session:
client_reference_id carries only the pseudonymous identifier, which is not PII.
client_reference_id must be 200 characters or fewer (a Stripe limit). The _traceten_vid cookie
is a UUID and is well within this limit. If you already use client_reference_id for your own
order number, attribution falls back to the email-matching path automatically.client_reference_id matching covers both one-time Checkout (Stripe Checkout, Payment Links,
and Pricing Tables in mode: 'payment', the guest-checkout case above) and subscription
Checkout (mode: 'subscription'). For subscriptions, Traceten records the reference when the
Checkout completes and applies it to the subscription’s first invoice, so the first charge is
attributed exactly once. Set client_reference_id the same way for both. If a subscription has no
reference (for example, created directly via the Stripe API or dashboard rather than Checkout),
Traceten falls back to email-based matching automatically. Attribution of recurring renewal
charges is tracked separately.Invoice and subscription payments: call identify()
invoice.payment_succeeded events (subscription renewals and manually-issued
invoices) never carry a client_reference_id (it is a Checkout Session
field). For these payments the only way Traceten can match the revenue to a
session is the email hash: call traceten.identify({ email }) during the
visitor’s session, using the same email the customer pays with.
traceten.identify() sends the email to our
edge over HTTPS; the hash is computed there, on our servers, not in the browser.
We clear the plaintext from memory immediately and never write it to storage or
to a log. But it is transmitted, and a privacy notice that implies otherwise
would be wrong.
There are two digests, and only one is keyed. Every payment is matched on
SHA-256 of the email plus a per-site salt. Alongside it we compute an
HMAC-SHA-256 under a per-site key, which is what identify() produces and
what is tried first. A salt and a key are different constructions; only the
second is keyed. The key is generated for every site when the site is created,
so this does not depend on you having called identify() before.
Without an
identify() call, a renewal invoice has no session to match and is recorded as
unattributed. The subscription’s first charge is still attributed exactly when the
original Checkout passed client_reference_id; it is the recurring renewals that need
identify() to stay attributed.Testing your setup
For the fastest reliable test path, use a Stripe Payment Link. The Traceten snippet auto-appends the visitor’s_traceten_vid value to Payment Link URLs
on your site, so client_reference_id is populated with no server code. Complete
a purchase through a Payment Link and the resulting revenue row should attribute
to the originating AI session.
The purchase arrives on the webhook endpoint Traceten created during connect,
so it should show up within a minute or so of completing checkout.
If you create Checkout Sessions server-side instead, you must pass the cookie
yourself. See the guest-checkout section
above. A server-created session that does not set client_reference_id (and whose
visitor never called identify()) will record as unattributed.
You can confirm coverage on the dashboard’s Attribution coverage card, which
shows what share of revenue Traceten tied back to a session and how much is still
unattributed.
Backfilling historical orders
A webhook endpoint only delivers events created after it exists, so anything that happened before you connected has to be imported. After connecting Stripe, Traceten automatically backfills the last 90 days of:- completed Checkout Sessions: one-time purchases, Payment Links and subscription checkouts. These carry
client_reference_id, so a historical order can be attributed to the exact AI session that produced it. - paid invoices: subscription charges and manually issued invoices, attributed by email hash.
Rotating the key
To replace the key, create a new restricted key in Stripe with the same permissions, then pick Stripe on Settings → Integrations for that site, click Replace key, and paste it in. Traceten creates the replacement endpoint first, stores it, and only then deletes the one the old key created, so nothing stops delivering in between and stale endpoints do not accumulate on your Stripe account. For a moment both endpoints exist and a payment may be delivered twice; Traceten ignores the duplicate. Your imported history and your backfill are kept; you do not need to disconnect first. Delete the old key in Stripe once the new one shows as connected.Using a processor we do not integrate with
Stripe, Shopify, Lemon Squeezy, Polar and Paddle have built-in integrations. Dodo has a documented path with no server of yours required. For anything else, including Podia, Kajabi and your own checkout, send payments to the Payment API instead. Do not do both for the same processor. If you connect Stripe here and post Stripe payments to the Payment API, every payment is recorded twice and your revenue is overstated. The two paths deduplicate on different keys and cannot see each other. See avoiding duplicate payments.Disconnecting Stripe
To remove the Stripe integration:- Open Settings → Integrations in the dashboard and choose the site.
- Pick Stripe, then click Disconnect and confirm.
- Delete the restricted key in your Stripe dashboard.

